diff --git a/.flake8 b/.flake8
deleted file mode 100644
index cd8e21ed8..000000000
--- a/.flake8
+++ /dev/null
@@ -1,6 +0,0 @@
-[flake8]
-max-line-length = 120
-builtins = ufl
-exclude = doc/sphinx/source/conf.py
-per-file-ignores =
- */__init__.py: F401
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 000000000..4e7559dcb
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,15 @@
+# To get started with Dependabot version updates, you'll need to specify which
+# package ecosystems to update and where the package manifests are located.
+# Please see the documentation for all configuration options:
+# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
+
+version: 2
+updates:
+ - package-ecosystem: "github-actions" # See documentation for possible values
+ directory: "/" # Location of package manifests
+ schedule:
+ interval: "weekly"
+ - package-ecosystem: "pip" # See documentation for possible values
+ directory: "/" # Location of package manifests
+ schedule:
+ interval: "weekly"
diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml
index 7d0847457..7301b1b6c 100644
--- a/.github/workflows/build-wheels.yml
+++ b/.github/workflows/build-wheels.yml
@@ -11,11 +11,11 @@ on:
default: "main"
type: string
test_pypi_publish:
- description: "Publish to Test PyPi (true | false)"
+ description: "Publish to Test PyPi"
default: false
type: boolean
pypi_publish:
- description: "Publish to PyPi (true | false)"
+ description: "Publish to PyPi"
default: false
type: boolean
@@ -26,11 +26,11 @@ on:
default: "main"
type: string
test_pypi_publish:
- description: "Publish to Test PyPi (true | false)"
+ description: "Publish to Test PyPi"
default: false
type: boolean
pypi_publish:
- description: "Publish to PyPi (true | false))"
+ description: "Publish to PyPi"
default: false
type: boolean
@@ -54,28 +54,44 @@ jobs:
with:
path: dist/*
- upload_pypi:
- name: Upload to PyPI (optional)
+ upload_test_pypi:
+ name: Upload to test PyPI (optional)
+ if: ${{ github.event.inputs.test_pypi_publish == 'true' }}
needs: [build]
runs-on: ubuntu-latest
+ environment:
+ name: testpypi
+ url: https://test.pypi.org/p/fenics-ufl
+ permissions:
+ id-token: write
+
steps:
- uses: actions/download-artifact@v4
with:
name: artifact
path: dist
- - name: Push to PyPI
+ - name: Push to test PyPI
uses: pypa/gh-action-pypi-publish@release/v1
- if: ${{ github.event.inputs.pypi_publish == 'true' }}
with:
- user: __token__
- password: ${{ secrets.PYPI_TOKEN }}
- repository_url: https://upload.pypi.org/legacy/
+ repository-url: https://test.pypi.org/legacy/
+
+ upload_pypi:
+ name: Upload to PyPI (optional)
+ if: ${{ github.event.inputs.pypi_publish == 'true' }}
+ needs: [build]
+ runs-on: ubuntu-latest
+ environment:
+ name: pypi
+ url: https://pypi.org/p/fenics-ufl
+ permissions:
+ id-token: write
+
+ steps:
+ - uses: actions/download-artifact@v4
+ with:
+ name: artifact
+ path: dist
- - name: Push to Test PyPI
+ - name: Push to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
- if: ${{ github.event.inputs.test_pypi_publish == 'true' }}
- with:
- user: __token__
- password: ${{ secrets.PYPI_TEST_TOKEN }}
- repository_url: https://test.pypi.org/legacy/
diff --git a/.github/workflows/fenicsx-tests.yml b/.github/workflows/fenicsx-tests.yml
index 6363a55bf..192c4156d 100644
--- a/.github/workflows/fenicsx-tests.yml
+++ b/.github/workflows/fenicsx-tests.yml
@@ -22,7 +22,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
- python-version: 3.9
+ python-version: 3.12
- name: Install test dependencies
run: |
@@ -30,7 +30,7 @@ jobs:
- name: Install UFL
run: |
- pip3 install .
+ pip3 install --break-system-packages .
- name: Install Basix
run: |
@@ -65,12 +65,12 @@ jobs:
- name: Install UFL
run: |
- pip3 install .
+ pip3 install --break-system-packages .
- name: Install Basix and FFCx
run: |
- python3 -m pip install git+https://github.com/FEniCS/basix.git
- python3 -m pip install git+https://github.com/FEniCS/ffcx.git
+ python3 -m pip install --break-system-packages git+https://github.com/FEniCS/basix.git
+ python3 -m pip install --break-system-packages git+https://github.com/FEniCS/ffcx.git
- name: Clone DOLFINx
uses: actions/checkout@v4
@@ -83,6 +83,7 @@ jobs:
cmake -G Ninja -DCMAKE_BUILD_TYPE=Developer -B build -S dolfinx/cpp/
cmake --build build
cmake --install build
- python3 -m pip -v install --no-build-isolation --check-build-dependencies --config-settings=cmake.build-type="Developer" dolfinx/python/
+ python3 -m pip install --break-system-packages -r dolfinx/python/build-requirements.txt
+ python3 -m pip -v install --break-system-packages --no-build-isolation --check-build-dependencies --config-settings=cmake.build-type="Developer" dolfinx/python/[ci]
- 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 e6f23ec0b..127601b1b 100644
--- a/.github/workflows/pythonapp.yml
+++ b/.github/workflows/pythonapp.yml
@@ -21,6 +21,14 @@ jobs:
matrix:
os: [ubuntu-latest, macos-latest]
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
+ exclude:
+ - os: macos-latest
+ python-version: '3.8'
+ - os: macos-latest
+ python-version: '3.9'
+ include:
+ - os: windows-latest
+ python-version: '3.11'
steps:
- uses: actions/checkout@v4
@@ -28,22 +36,20 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- - name: Lint with flake8
+ - name: Lint with ruff
run: |
- python -m pip install flake8
- flake8 --statistics .
- - name: Check documentation style
- run: |
- python -m pip install pydocstyle[toml]
- python -m pydocstyle ufl/
+ pip install ruff
+ ruff check .
+ ruff format --check .
- name: Install UFL
run: python -m pip install .[ci]
- name: Run unit tests
run: |
python -m pytest -n auto --cov=ufl/ --junitxml=junit/test-results-${{ matrix.os }}-${{ matrix.python-version }}.xml test/
- name: Upload to Coveralls
- if: ${{ github.repository == 'FEniCS/ufl' && github.head_ref == '' && matrix.os == 'ubuntu-latest' && matrix.python-version == '3.8' }}
+ if: ${{ github.repository == 'FEniCS/ufl' && github.head_ref == '' && matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12' }}
env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
run: coveralls
@@ -61,25 +67,25 @@ jobs:
if-no-files-found: error
- name: Checkout FEniCS/docs
- if: ${{ github.repository == 'FEniCS/ufl' && ( github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') ) && runner.os == 'Linux' && matrix.python-version == 3.8 }}
+ if: ${{ github.repository == 'FEniCS/ufl' && ( github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') ) && runner.os == 'Linux' && matrix.python-version == 3.12 }}
uses: actions/checkout@v4
with:
repository: "FEniCS/docs"
path: "docs"
ssh-key: "${{ secrets.SSH_GITHUB_DOCS_PRIVATE_KEY }}"
- name: Set version name
- if: ${{ github.repository == 'FEniCS/ufl' && ( github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') ) && runner.os == 'Linux' && matrix.python-version == 3.8 }}
+ if: ${{ github.repository == 'FEniCS/ufl' && ( github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') ) && runner.os == 'Linux' && matrix.python-version == 3.12 }}
run: |
echo "VERSION_NAME=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: Copy documentation into repository
- if: ${{ github.repository == 'FEniCS/ufl' && ( github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') ) && runner.os == 'Linux' && matrix.python-version == 3.8 }}
+ if: ${{ github.repository == 'FEniCS/ufl' && ( github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') ) && runner.os == 'Linux' && matrix.python-version == 3.12 }}
run: |
cd docs
git rm -r --ignore-unmatch ufl/${{ env.VERSION_NAME }}
mkdir -p ufl/${{ env.VERSION_NAME }}
cp -r ../doc/sphinx/build/html/* ufl/${{ env.VERSION_NAME }}
- name: Commit and push documentation to FEniCS/docs
- if: ${{ github.repository == 'FEniCS/ufl' && ( github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') ) && runner.os == 'Linux' && matrix.python-version == 3.8 }}
+ if: ${{ github.repository == 'FEniCS/ufl' && ( github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') ) && runner.os == 'Linux' && matrix.python-version == 3.12 }}
run: |
cd docs
git config --global user.email "fenics@github.com"
diff --git a/.github/workflows/tsfc-tests.yml b/.github/workflows/tsfc-tests.yml
index 06b10a13d..98fd4696b 100644
--- a/.github/workflows/tsfc-tests.yml
+++ b/.github/workflows/tsfc-tests.yml
@@ -17,9 +17,9 @@ jobs:
CXX: g++-10
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Set up Python
- uses: actions/setup-python@v3
+ uses: actions/setup-python@v5
with:
python-version: 3.9
@@ -28,7 +28,7 @@ jobs:
pip3 install .
- name: Clone tsfc
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
path: ./tsfc
repository: firedrakeproject/tsfc
diff --git a/README.md b/README.md
index 5da66e02f..ad6cb8c89 100644
--- a/README.md
+++ b/README.md
@@ -11,12 +11,10 @@ https://www.fenicsproject.org
[![UFL CI](https://github.com/FEniCS/ufl/workflows/UFL%20CI/badge.svg)](https://github.com/FEniCS/ufl/workflows/UFL%20CI)
[![Coverage Status](https://coveralls.io/repos/github/FEniCS/ufl/badge.svg?branch=master)](https://coveralls.io/github/FEniCS/ufl?branch=master)
-[![Documentation Status](https://readthedocs.org/projects/fenics-ufl/badge/?version=latest)](https://fenics.readthedocs.io/projects/ufl/en/latest/?badge=latest)
## Documentation
-Documentation can be viewed at https://fenics-ufl.readthedocs.org/.
-
+Documentation can be viewed at https://docs.fenicsproject.org
## License
diff --git a/demo/Constant.py b/demo/Constant.py
index 96acaf22c..09f0d515f 100644
--- a/demo/Constant.py
+++ b/demo/Constant.py
@@ -18,15 +18,27 @@
# Modified by Martin Sandve Alnes, 2009
#
# Test form for scalar and vector constants.
-from ufl import (Coefficient, Constant, FunctionSpace, Mesh, TestFunction, TrialFunction, VectorConstant, 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, (), identity_pullback, H1)
-domain = Mesh(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/ConvectionJacobi.py b/demo/ConvectionJacobi.py
index 5f3b2da10..f058ca233 100644
--- a/demo/ConvectionJacobi.py
+++ b/demo/ConvectionJacobi.py
@@ -2,13 +2,23 @@
# Author: Martin Sandve Alnes
# Date: 2008-10-03
#
-from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, 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, (2, ), identity_pullback, H1)
-domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+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 c88108a17..bbe68b292 100644
--- a/demo/ConvectionJacobi2.py
+++ b/demo/ConvectionJacobi2.py
@@ -7,8 +7,8 @@
from ufl.pullback import identity_pullback
from ufl.sobolevspace import H1
-element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
-domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+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 e83e60698..59b5e233d 100644
--- a/demo/ConvectionVector.py
+++ b/demo/ConvectionVector.py
@@ -7,8 +7,8 @@
from ufl.pullback import identity_pullback
from ufl.sobolevspace import H1
-element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
-domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+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 73513d5d6..324b2cefd 100644
--- a/demo/Elasticity.py
+++ b/demo/Elasticity.py
@@ -8,8 +8,8 @@
from ufl.pullback import identity_pullback
from ufl.sobolevspace import H1
-element = FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)
-domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1))
+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 30f1ade40..de2de347c 100644
--- a/demo/EnergyNorm.py
+++ b/demo/EnergyNorm.py
@@ -23,7 +23,7 @@
from ufl.sobolevspace import H1
element = FiniteElement("Lagrange", tetrahedron, 1, (), identity_pullback, H1)
-domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), 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 33625545b..f440f303d 100644
--- a/demo/Equation.py
+++ b/demo/Equation.py
@@ -34,13 +34,25 @@
# 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, FunctionSpace, Mesh, TestFunction, TrialFunction, 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, (), identity_pullback, H1)
-domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), 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 c56d27376..e5086c1a9 100644
--- a/demo/ExplicitConvection.py
+++ b/demo/ExplicitConvection.py
@@ -2,13 +2,23 @@
# Author: Martin Sandve Alnes
# Date: 2008-10-03
#
-from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, 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, (2, ), identity_pullback, H1)
-domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+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/FunctionOperators.py b/demo/FunctionOperators.py
index 075e7b8c1..4aeb4d621 100644
--- a/demo/FunctionOperators.py
+++ b/demo/FunctionOperators.py
@@ -16,13 +16,25 @@
# along with UFL. If not, see .
#
# Test form for operators on Coefficients.
-from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, 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, (), identity_pullback, H1)
-domain = Mesh(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)
@@ -30,4 +42,7 @@
f = Coefficient(space)
g = Coefficient(space)
-a = sqrt(1 / max_value(1 / f, -1 / f)) * sqrt(g) * dot(grad(v), grad(u)) * dx + v * u * sqrt(f * g) * g * dx
+a = (
+ sqrt(1 / max_value(1 / f, -1 / f)) * sqrt(g) * dot(grad(v), grad(u)) * dx
+ + v * u * sqrt(f * g) * g * dx
+)
diff --git a/demo/H1norm.py b/demo/H1norm.py
index 9da6b28e4..769607aff 100644
--- a/demo/H1norm.py
+++ b/demo/H1norm.py
@@ -8,7 +8,7 @@
from ufl.sobolevspace import H1
element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
-domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), 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 8aa3ee5d2..1d53906c9 100644
--- a/demo/HarmonicMap.py
+++ b/demo/HarmonicMap.py
@@ -9,9 +9,9 @@
from ufl.sobolevspace import H1
cell = triangle
-X = FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)
+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))
+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 dfc47c3b1..fc048868f 100644
--- a/demo/HarmonicMap2.py
+++ b/demo/HarmonicMap2.py
@@ -9,10 +9,10 @@
from ufl.sobolevspace import H1
cell = triangle
-X = FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)
+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))
+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 f695341b2..2a197b5d1 100644
--- a/demo/Heat.py
+++ b/demo/Heat.py
@@ -20,22 +20,33 @@
# 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, FunctionSpace, Mesh, TestFunction, TrialFunction, 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, (), identity_pullback, H1)
-domain = Mesh(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) # Test function
u1 = TrialFunction(space) # Value at t_n
-u0 = Coefficient(space) # Value at t_n-1
-c = Coefficient(space) # Heat conductivity
-f = Coefficient(space) # Heat source
-k = Constant(domain) # Time step
+u0 = Coefficient(space) # Value at t_n-1
+c = Coefficient(space) # Heat conductivity
+f = Coefficient(space) # Heat source
+k = Constant(domain) # Time step
a = v * u1 * dx + k * c * dot(grad(v), grad(u1)) * dx
L = v * u0 * dx + k * v * f * dx
diff --git a/demo/HornSchunck.py b/demo/HornSchunck.py
index d3ec43840..5238ed04e 100644
--- a/demo/HornSchunck.py
+++ b/demo/HornSchunck.py
@@ -3,7 +3,18 @@
# http://code.google.com/p/debiosee/wiki/DemosOptiocFlowHornSchunck
# but not tested so this could contain errors!
#
-from ufl import Coefficient, Constant, FunctionSpace, Mesh, 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
@@ -11,8 +22,8 @@
# Finite element spaces for scalar and vector fields
cell = triangle
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))
+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)
@@ -29,8 +40,7 @@
lamda = Constant(domain)
# Coefficiental to minimize
-M = (dot(u, grad(I1)) + (I1 - I0))**2 * dx\
- + lamda * inner(grad(u), grad(u)) * dx
+M = (dot(u, grad(I1)) + (I1 - I0)) ** 2 * dx + lamda * inner(grad(u), grad(u)) * dx
# Derived linear system
L = derivative(M, u)
diff --git a/demo/HyperElasticity.py b/demo/HyperElasticity.py
index abbc8314c..e4882d2b6 100644
--- a/demo/HyperElasticity.py
+++ b/demo/HyperElasticity.py
@@ -3,22 +3,45 @@
# 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 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.pullback import identity_pullback
from ufl.sobolevspace import H1
# Cell and its properties
cell = tetrahedron
-domain = Mesh(FiniteElement("Lagrange", cell, 1, (3, ), identity_pullback, H1))
-d = cell.geometric_dimension()
+domain = Mesh(FiniteElement("Lagrange", cell, 1, (3,), identity_pullback, H1))
+d = 3
N = FacetNormal(domain)
x = SpatialCoordinate(domain)
# Elements
-u_element = FiniteElement("Lagrange", cell, 2, (3, ), identity_pullback, H1)
+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)
@@ -79,7 +102,9 @@
Ef = A * E * A.T
# Strain energy function W(Q(Ef))
-Q = c00 * Ef[0, 0]**2 + c11 * Ef[1, 1]**2 + c22 * Ef[2, 2]**2 # FIXME: insert some simple law here
+Q = (
+ c00 * Ef[0, 0] ** 2 + c11 * Ef[1, 1] ** 2 + c22 * Ef[2, 2] ** 2
+) # FIXME: insert some simple law here
W = (K / 2) * (exp(Q) - 1) # + p stuff
# First Piola-Kirchoff stress tensor
@@ -87,13 +112,15 @@
# Acceleration term discretized with finite differences
k = dt / rho
-acc = (u - 2 * up + upp)
+acc = u - 2 * up + upp
# Residual equation # FIXME: Can contain errors, not tested!
-a_F = inner(acc, v) * dx \
- + k * inner(P, grad(v)) * dx \
- - k * dot(J * Finv * T, v) * ds(0) \
+a_F = (
+ inner(acc, v) * dx
+ + k * inner(P, grad(v)) * dx
+ - k * dot(J * Finv * T, v) * ds(0)
- k * dot(J * Finv * p0 * N, v) * ds(1)
+)
# Jacobi matrix of residual equation
a_J = derivative(a_F, u, w)
diff --git a/demo/HyperElasticity1D.py b/demo/HyperElasticity1D.py
index 9dfcbed96..f2cd1ab52 100644
--- a/demo/HyperElasticity1D.py
+++ b/demo/HyperElasticity1D.py
@@ -9,14 +9,14 @@
cell = interval
element = FiniteElement("Lagrange", cell, 2, (), identity_pullback, H1)
-domain = Mesh(FiniteElement("Lagrange", cell, 1, (1, ), identity_pullback, H1))
+domain = Mesh(FiniteElement("Lagrange", cell, 1, (1,), identity_pullback, H1))
space = FunctionSpace(domain, element)
u = Coefficient(space)
b = Constant(domain)
K = Constant(domain)
-E = u.dx(0) + u.dx(0)**2 / 2
+E = u.dx(0) + u.dx(0) ** 2 / 2
E = variable(E)
Q = b * E**2
psi = K * (exp(Q) - 1)
diff --git a/demo/L2norm.py b/demo/L2norm.py
index 31050a9df..cd492f647 100644
--- a/demo/L2norm.py
+++ b/demo/L2norm.py
@@ -8,7 +8,7 @@
from ufl.sobolevspace import H1
element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
-domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), 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 4f083140b..eb20c5afb 100644
--- a/demo/Mass.py
+++ b/demo/Mass.py
@@ -26,7 +26,7 @@
from ufl.sobolevspace import H1
element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
-domain = Mesh(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/MassAD.py b/demo/MassAD.py
index ed36e8c2c..9f90b5eec 100644
--- a/demo/MassAD.py
+++ b/demo/MassAD.py
@@ -8,7 +8,7 @@
from ufl.sobolevspace import H1
element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
-domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), 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 6fe1a96a3..466935be0 100644
--- a/demo/MixedElasticity.py
+++ b/demo/MixedElasticity.py
@@ -17,8 +17,20 @@
#
# First added: 2008-10-03
# Last changed: 2011-07-22
-from ufl import (FunctionSpace, Mesh, TestFunctions, TrialFunctions, 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
@@ -36,19 +48,22 @@ def skw(tau):
# Finite element exterior calculus syntax
r = 1
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)
+V = FiniteElement("Discontinuous Lagrange", cell, r - 1, (3,), identity_pullback, L2)
+Q = FiniteElement("Discontinuous Lagrange", cell, r - 1, (3,), identity_pullback, L2)
W = MixedElement([S, V, Q])
-domain = Mesh(FiniteElement("Lagrange", cell, 1, (3, ), identity_pullback, H1))
+domain = Mesh(FiniteElement("Lagrange", cell, 1, (3,), identity_pullback, H1))
space = FunctionSpace(domain, W)
(sigma, u, gamma) = TrialFunctions(space)
(tau, v, eta) = TestFunctions(space)
a = (
- inner(sigma, tau) - tr(sigma) * tr(tau) + dot(
- div(tau), u
- ) - dot(div(sigma), v) + inner(skw(tau), gamma) + inner(skw(sigma), eta)
+ inner(sigma, tau)
+ - tr(sigma) * tr(tau)
+ + dot(div(tau), u)
+ - dot(div(sigma), v)
+ + inner(skw(tau), gamma)
+ + inner(skw(sigma), eta)
) * dx
diff --git a/demo/MixedPoisson.py b/demo/MixedPoisson.py
index 1580372a3..6e1c1730f 100644
--- a/demo/MixedPoisson.py
+++ b/demo/MixedPoisson.py
@@ -23,17 +23,27 @@
# a mixed formulation of Poisson's equation with BDM
# (Brezzi-Douglas-Marini) elements.
#
-from ufl import Coefficient, FunctionSpace, Mesh, TestFunctions, TrialFunctions, 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, (2, ), contravariant_piola, HDiv)
+BDM1 = FiniteElement("Brezzi-Douglas-Marini", cell, 1, (2,), contravariant_piola, HDiv)
DG0 = FiniteElement("Discontinuous Lagrange", cell, 0, (), identity_pullback, H1)
element = MixedElement([BDM1, DG0])
-domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1))
+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 29268a309..fc9deb84d 100644
--- a/demo/MixedPoisson2.py
+++ b/demo/MixedPoisson2.py
@@ -3,16 +3,27 @@
# Modified by: Martin Sandve Alnes
# Date: 2009-02-12
#
-from ufl import FacetNormal, FunctionSpace, Mesh, TestFunctions, TrialFunctions, 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, (3, ), contravariant_piola, HDiv)
+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))
+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 14dfa5f56..e1121d9c9 100644
--- a/demo/NavierStokes.py
+++ b/demo/NavierStokes.py
@@ -21,14 +21,24 @@
#
# The bilinear form for the nonlinear term in the
# Navier-Stokes equations with fixed convective velocity.
-from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, 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 = FiniteElement("Lagrange", cell, 1, (3, ), identity_pullback, H1)
-domain = Mesh(FiniteElement("Lagrange", cell, 1, (3, ), identity_pullback, H1))
+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 d384c4315..95f255933 100644
--- a/demo/NeumannProblem.py
+++ b/demo/NeumannProblem.py
@@ -17,13 +17,24 @@
#
# 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, 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 = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
-domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+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 7604459b5..56ef5cb9a 100644
--- a/demo/NonlinearPoisson.py
+++ b/demo/NonlinearPoisson.py
@@ -1,10 +1,20 @@
-from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, 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, (), identity_pullback, H1)
-domain = Mesh(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)
@@ -12,6 +22,5 @@
u0 = Coefficient(space)
f = Coefficient(space)
-a = (1 + u0**2) * dot(grad(v), grad(u)) * dx \
- + 2 * u0 * u * dot(grad(v), grad(u0)) * dx
+a = (1 + u0**2) * dot(grad(v), grad(u)) * dx + 2 * u0 * u * dot(grad(v), grad(u0)) * dx
L = v * f * dx - (1 + u0**2) * dot(grad(v), grad(u0)) * dx
diff --git a/demo/Poisson.py b/demo/Poisson.py
index 779273391..bfae70f8b 100644
--- a/demo/Poisson.py
+++ b/demo/Poisson.py
@@ -21,13 +21,23 @@
# Last changed: 2009-03-02
#
# The bilinear form a(v, u) and linear form L(v) for Poisson's equation.
-from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, 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, (), identity_pullback, H1)
-domain = Mesh(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/PoissonDG.py b/demo/PoissonDG.py
index fd289213a..bfe01c7a0 100644
--- a/demo/PoissonDG.py
+++ b/demo/PoissonDG.py
@@ -21,15 +21,31 @@
# 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, FunctionSpace, Mesh, TestFunction, TrialFunction, 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, (), identity_pullback, L2)
-domain = Mesh(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)
@@ -44,12 +60,14 @@
alpha = 4.0
gamma = 8.0
-a = inner(grad(v), grad(u)) * dx \
- - inner(avg(grad(v)), jump(u, n)) * dS \
- - inner(jump(v, n), avg(grad(u))) * dS \
- + alpha / h('+') * dot(jump(v, n), jump(u, n)) * dS \
- - inner(grad(v), u * n) * ds \
- - inner(v * n, grad(u)) * ds \
+a = (
+ inner(grad(v), grad(u)) * dx
+ - inner(avg(grad(v)), jump(u, n)) * dS
+ - inner(jump(v, n), avg(grad(u))) * dS
+ + alpha / h("+") * dot(jump(v, n), jump(u, n)) * dS
+ - inner(grad(v), u * n) * ds
+ - inner(v * n, grad(u)) * ds
+ gamma / h * v * u * ds
+)
L = v * f * dx + v * gN * ds
diff --git a/demo/PoissonSystem.py b/demo/PoissonSystem.py
index 29967d025..9bcd06ad3 100644
--- a/demo/PoissonSystem.py
+++ b/demo/PoissonSystem.py
@@ -21,14 +21,25 @@
#
# 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, 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 = FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)
-domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1))
+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 106f1f799..dde304d99 100644
--- a/demo/PowAD.py
+++ b/demo/PowAD.py
@@ -2,13 +2,22 @@
# Author: Martin Sandve Alnes
# Date: 2008-10-03
#
-from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, 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, (), identity_pullback, H1)
-domain = Mesh(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/ProjectionSystem.py b/demo/ProjectionSystem.py
index 7548a0b22..5211abb84 100644
--- a/demo/ProjectionSystem.py
+++ b/demo/ProjectionSystem.py
@@ -4,7 +4,7 @@
from ufl.sobolevspace import H1
element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
-domain = Mesh(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)
diff --git a/demo/QuadratureElement.py b/demo/QuadratureElement.py
index 4e01cd31b..5ca50134f 100644
--- a/demo/QuadratureElement.py
+++ b/demo/QuadratureElement.py
@@ -20,17 +20,28 @@
#
# 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, FunctionSpace, Mesh, TestFunction, TrialFunction, 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, (), identity_pullback, H1)
-domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
space = FunctionSpace(domain, element)
QE = FiniteElement("Quadrature", triangle, 2, (), identity_pullback, H1)
-sig = FiniteElement("Quadrature", triangle, 1, (2, ), identity_pullback, H1)
+sig = FiniteElement("Quadrature", triangle, 1, (2,), identity_pullback, H1)
qe_space = FunctionSpace(domain, QE)
sig_space = FunctionSpace(domain, sig)
@@ -42,5 +53,8 @@
sig0 = Coefficient(sig_space)
f = Coefficient(space)
-a = v.dx(i) * C * u.dx(i) * dx(metadata={"quadrature_degree": 2}) + v.dx(i) * 2 * u0 * u * u0.dx(i) * dx
+a = (
+ v.dx(i) * C * u.dx(i) * dx(metadata={"quadrature_degree": 2})
+ + v.dx(i) * 2 * u0 * u * u0.dx(i) * dx
+)
L = v * f * dx - dot(grad(v), sig0) * dx(metadata={"quadrature_degree": 1})
diff --git a/demo/Stiffness.py b/demo/Stiffness.py
index 32bf00a54..f96fa6e32 100644
--- a/demo/Stiffness.py
+++ b/demo/Stiffness.py
@@ -8,7 +8,7 @@
from ufl.sobolevspace import H1
element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
-domain = Mesh(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/StiffnessAD.py b/demo/StiffnessAD.py
index 59a8bcba4..c95b94684 100644
--- a/demo/StiffnessAD.py
+++ b/demo/StiffnessAD.py
@@ -2,13 +2,24 @@
# Author: Martin Sandve Alnes
# Date: 2008-10-30
#
-from ufl import Coefficient, FunctionSpace, Mesh, 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, (), identity_pullback, H1)
-domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), 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 b4d240ffd..4a2c185ac 100644
--- a/demo/Stokes.py
+++ b/demo/Stokes.py
@@ -20,16 +20,28 @@
#
# 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, FunctionSpace, Mesh, TestFunctions, TrialFunctions, 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 = FiniteElement("Lagrange", cell, 2, (2, ), identity_pullback, H1)
+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))
+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 949551846..667ca729b 100644
--- a/demo/StokesEquation.py
+++ b/demo/StokesEquation.py
@@ -19,17 +19,30 @@
# 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, FunctionSpace, Mesh, TestFunctions, TrialFunctions, 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 = FiniteElement("Lagrange", cell, 2, (2, ), identity_pullback, H1)
+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))
+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 4205a7790..39dca0653 100644
--- a/demo/SubDomain.py
+++ b/demo/SubDomain.py
@@ -23,7 +23,7 @@
from ufl.sobolevspace import H1
element = FiniteElement("Lagrange", tetrahedron, 1, (), identity_pullback, H1)
-domain = Mesh(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/SubDomains.py b/demo/SubDomains.py
index 55e9ddbe5..f4d76da7a 100644
--- a/demo/SubDomains.py
+++ b/demo/SubDomains.py
@@ -17,17 +17,23 @@
#
# This simple example illustrates how forms can be defined on different sub domains.
# It is supported for all three integral types.
-from ufl import FunctionSpace, Mesh, TestFunction, TrialFunction, 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("Lagrange", tetrahedron, 1, (), identity_pullback, H1)
-domain = Mesh(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)
u = TrialFunction(space)
-a = v * u * dx(0) + 10.0 * v * u * dx(1) + v * u * ds(0) + 2.0 * v * u * ds(1)\
- + v('+') * u('+') * dS(0) + 4.3 * v('+') * u('+') * dS(1)
+a = (
+ v * u * dx(0)
+ + 10.0 * v * u * dx(1)
+ + v * u * ds(0)
+ + 2.0 * v * u * ds(1)
+ + v("+") * u("+") * dS(0)
+ + 4.3 * v("+") * u("+") * dS(1)
+)
diff --git a/demo/TensorWeightedPoisson.py b/demo/TensorWeightedPoisson.py
index 6ebc8e3c1..a46516dd4 100644
--- a/demo/TensorWeightedPoisson.py
+++ b/demo/TensorWeightedPoisson.py
@@ -17,14 +17,24 @@
#
# The bilinear form a(v, u) and linear form L(v) for
# tensor-weighted Poisson's equation.
-from ufl import Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, 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, (), identity_pullback, H1)
P0 = FiniteElement("Discontinuous Lagrange", triangle, 0, (2, 2), identity_pullback, L2)
-domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+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 f5d9b863f..ca08db9ac 100644
--- a/demo/VectorLaplaceGradCurl.py
+++ b/demo/VectorLaplaceGradCurl.py
@@ -18,7 +18,18 @@
# 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, FunctionSpace, Mesh, TestFunctions, TrialFunctions, 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
@@ -29,7 +40,9 @@ def HodgeLaplaceGradCurl(space, fspace):
sigma, u = TrialFunctions(space)
f = Coefficient(fspace)
- a = (inner(tau, sigma) - inner(grad(tau), u) + inner(v, grad(sigma)) + inner(curl(v), curl(u))) * dx
+ a = (
+ inner(tau, sigma) - inner(grad(tau), u) + inner(v, grad(sigma)) + inner(curl(v), curl(u))
+ ) * dx
L = inner(v, f) * dx
return a, L
@@ -39,11 +52,11 @@ def HodgeLaplaceGradCurl(space, fspace):
order = 1
GRAD = FiniteElement("Lagrange", cell, order, (), identity_pullback, H1)
-CURL = FiniteElement("N1curl", cell, order, (3, ), covariant_piola, HCurl)
+CURL = FiniteElement("N1curl", cell, order, (3,), covariant_piola, HCurl)
-VectorLagrange = FiniteElement("Lagrange", cell, order + 1, (3, ), identity_pullback, H1)
+VectorLagrange = FiniteElement("Lagrange", cell, order + 1, (3,), identity_pullback, H1)
-domain = Mesh(FiniteElement("Lagrange", cell, 1, (3, ), identity_pullback, H1))
+domain = Mesh(FiniteElement("Lagrange", cell, 1, (3,), identity_pullback, H1))
space = FunctionSpace(domain, MixedElement([GRAD, CURL]))
fspace = FunctionSpace(domain, VectorLagrange)
diff --git a/demo/_TensorProductElement.py b/demo/_TensorProductElement.py
index 9e6fb6ef0..c32b1fd09 100644
--- a/demo/_TensorProductElement.py
+++ b/demo/_TensorProductElement.py
@@ -17,8 +17,17 @@
#
# First added: 2012-08-16
# Last changed: 2012-08-16
-from ufl import (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
diff --git a/doc/sphinx/source/conf.py b/doc/sphinx/source/conf.py
index 60a0af8ee..5ad9345db 100644
--- a/doc/sphinx/source/conf.py
+++ b/doc/sphinx/source/conf.py
@@ -18,43 +18,43 @@
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
+# sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
+# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
- 'sphinx.ext.autodoc',
- 'sphinx.ext.doctest',
- 'sphinx.ext.coverage',
- 'sphinx.ext.mathjax',
- 'sphinx.ext.viewcode',
+ "sphinx.ext.autodoc",
+ "sphinx.ext.doctest",
+ "sphinx.ext.coverage",
+ "sphinx.ext.mathjax",
+ "sphinx.ext.viewcode",
]
# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+templates_path = ["_templates"]
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
# source_suffix = ['.rst', '.md']
-source_suffix = '.rst'
+source_suffix = ".rst"
# The encoding of source files.
-#source_encoding = 'utf-8-sig'
+# source_encoding = 'utf-8-sig'
# The master toctree document.
-master_doc = 'index'
+master_doc = "index"
# General information about the project.
-project = u'Unified Form Language (UFL)'
+project = "Unified Form Language (UFL)"
this_year = datetime.date.today().year
-copyright = u'%s, FEniCS Project' % this_year
-author = u'FEniCS Project'
+copyright = "%s, FEniCS Project" % this_year
+author = "FEniCS Project"
version = importlib.metadata.version("fenics-ufl")
release = version
@@ -68,9 +68,9 @@
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
-#today = ''
+# today = ''
# Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
+# today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
@@ -78,27 +78,27 @@
# The reST default role (used for this markup: `text`) to use for all
# documents.
-#default_role = None
+# default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
+# add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
-#add_module_names = True
+# add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
-#show_authors = False
+# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
+pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
+# modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
-#keep_warnings = False
+# keep_warnings = False
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
@@ -108,31 +108,31 @@
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
-#html_theme = 'alabaster'
+# html_theme = 'alabaster'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
-#html_theme_options = {}
+# html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
+# html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# " v documentation".
-#html_title = None
+# html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
-#html_short_title = None
+# html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
-#html_logo = None
+# html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
-#html_favicon = None
+# html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
@@ -142,109 +142,111 @@
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
-#html_extra_path = []
+# html_extra_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
+# html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
-#html_use_smartypants = True
+# html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
+# html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
-#html_additional_pages = {}
+# html_additional_pages = {}
# If false, no module index is generated.
-#html_domain_indices = True
+# html_domain_indices = True
# If false, no index is generated.
-#html_use_index = True
+# html_use_index = True
# If true, the index is split into individual pages for each letter.
-#html_split_index = False
+# html_split_index = False
# If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
+# html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
+# html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
+# html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
-#html_use_opensearch = ''
+# html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
+# html_file_suffix = None
# Language to be used for generating the HTML full-text search index.
# Sphinx supports the following languages:
# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'
-#html_search_language = 'en'
+# html_search_language = 'en'
# A dictionary with options for the search language support, empty by default.
# Now only 'ja' uses this config value
-#html_search_options = {'type': 'default'}
+# html_search_options = {'type': 'default'}
# The name of a javascript file (relative to the configuration directory) that
# implements a search results scorer. If empty, the default will be used.
-#html_search_scorer = 'scorer.js'
+# html_search_scorer = 'scorer.js'
# Output file base name for HTML help builder.
-htmlhelp_basename = 'UnifiedFormLanguageUFLdoc'
+htmlhelp_basename = "UnifiedFormLanguageUFLdoc"
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
-# The paper size ('letterpaper' or 'a4paper').
-#'papersize': 'letterpaper',
-
-# The font size ('10pt', '11pt' or '12pt').
-#'pointsize': '10pt',
-
-# Additional stuff for the LaTeX preamble.
-#'preamble': '',
-
-# Latex figure (float) alignment
-#'figure_align': 'htbp',
+ # The paper size ('letterpaper' or 'a4paper').
+ #'papersize': 'letterpaper',
+ # The font size ('10pt', '11pt' or '12pt').
+ #'pointsize': '10pt',
+ # Additional stuff for the LaTeX preamble.
+ #'preamble': '',
+ # Latex figure (float) alignment
+ #'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
- (master_doc, 'UnifiedFormLanguageUFL.tex', u'Unified Form Language (UFL) Documentation',
- u'FEniCS Project', 'manual'),
+ (
+ master_doc,
+ "UnifiedFormLanguageUFL.tex",
+ "Unified Form Language (UFL) Documentation",
+ "FEniCS Project",
+ "manual",
+ ),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
-#latex_logo = None
+# latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
-#latex_use_parts = False
+# latex_use_parts = False
# If true, show page references after internal links.
-#latex_show_pagerefs = False
+# latex_show_pagerefs = False
# If true, show URL addresses after external links.
-#latex_show_urls = False
+# latex_show_urls = False
# Documents to append as an appendix to all manuals.
-#latex_appendices = []
+# latex_appendices = []
# If false, no module index is generated.
-#latex_domain_indices = True
+# latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
@@ -252,12 +254,11 @@
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
- (master_doc, 'unifiedformlanguageufl', u'Unified Form Language (UFL) Documentation',
- [author], 1)
+ (master_doc, "unifiedformlanguageufl", "Unified Form Language (UFL) Documentation", [author], 1)
]
# If true, show URL addresses after external links.
-#man_show_urls = False
+# man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
@@ -266,19 +267,25 @@
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
- (master_doc, 'UnifiedFormLanguageUFL', u'Unified Form Language (UFL) Documentation',
- author, 'UnifiedFormLanguageUFL', 'One line description of project.',
- 'Miscellaneous'),
+ (
+ master_doc,
+ "UnifiedFormLanguageUFL",
+ "Unified Form Language (UFL) Documentation",
+ author,
+ "UnifiedFormLanguageUFL",
+ "One line description of project.",
+ "Miscellaneous",
+ ),
]
# Documents to append as an appendix to all manuals.
-#texinfo_appendices = []
+# texinfo_appendices = []
# If false, no module index is generated.
-#texinfo_domain_indices = True
+# texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
-#texinfo_show_urls = 'footnote'
+# texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
-#texinfo_no_detailmenu = False
+# texinfo_no_detailmenu = False
diff --git a/doc/sphinx/source/manual/form_language.rst b/doc/sphinx/source/manual/form_language.rst
index 3d20924de..1c9687893 100644
--- a/doc/sphinx/source/manual/form_language.rst
+++ b/doc/sphinx/source/manual/form_language.rst
@@ -764,11 +764,10 @@ Basic nonlinear functions
Some basic nonlinear functions are also available, their meaning mostly
obvious.
-* ``abs(f)``: the absolute value of f.
+The following functions are defined and should be imported from `ufl`
-* ``sign(f)``: the sign of f (+1 or -1).
-* ``pow(f, g)`` or ``f**g``: f to the power g, :math:`f^g`
+* ``sign(f)``: the sign of f (+1 or -1).
* ``sqrt(f)``: square root, :math:`\sqrt{f}`
@@ -806,6 +805,14 @@ obvious.
* ``bessel_K(nu, f)``: Modified Bessel function of the second kind, :math:`K_\nu(f)`
+while the following Python built in functions can be used without an import statement
+
+* ``abs(f)``: the absolute value of f.
+
+
+* ``pow(f, g)`` or ``f**g``: f to the power g, :math:`f^g`
+
+
These functions do not accept non-scalar operands or operands with free
indices or ``Argument`` dependencies.
diff --git a/pyproject.toml b/pyproject.toml
index 468cd0a10..555a334d2 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,15 +1,18 @@
[build-system]
-requires = ["setuptools>=62", "wheel", "pip>=22.3"]
+requires = ["setuptools>=62", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "fenics-ufl"
-version = "2023.3.0.dev0"
-authors = [{name="UFL contributors"}]
-maintainers = [{email="fenics-steering-council@googlegroups.com"}, {name="FEniCS Steering Council"}]
+version = "2024.3.0.dev0"
+authors = [{ name = "UFL contributors" }]
+maintainers = [
+ { email = "fenics-steering-council@googlegroups.com" },
+ { name = "FEniCS Steering Council" },
+]
description = "Unified Form Language"
-readme = "README.rst"
-license = {file = "COPYING.lesser"}
+readme = "README.md"
+license = { file = "COPYING.lesser" }
requires-python = ">=3.8.0"
dependencies = ["numpy"]
@@ -21,7 +24,7 @@ issues = "https://github.com/FEniCS/ufl/issues"
funding = "https://numfocus.org/donate"
[project.optional-dependencies]
-lint = ["flake8", "pydocstyle[toml]"]
+lint = ["ruff"]
docs = ["sphinx", "sphinx_rtd_theme"]
test = ["pytest"]
ci = [
@@ -44,8 +47,38 @@ packages = [
"ufl.utils",
]
-[tool.pydocstyle]
-convention = "google"
+[tool.ruff]
+line-length = 100
+indent-width = 4
-[tool.isort]
-line_length = 120
+[tool.ruff.format]
+docstring-code-format = true
+
+[tool.ruff.lint]
+select = [
+ # "N", # pep8-naming
+ "E", # pycodestyle
+ "W", # pycodestyle
+ "D", # pydocstyle
+ "F", # pyflakes
+ "I", # isort
+ "RUF", # Ruff-specific rules
+ # "UP", # pyupgrade
+ "ICN", # flake8-import-conventions
+ "NPY", # numpy-specific rules
+ "FLY", # use f-string not static joins
+ "LOG", # https://docs.astral.sh/ruff/rules/#flake8-logging-log
+ # "ISC", # https://docs.astral.sh/ruff/rules/#flake8-implicit-str-concat-isc
+ # "B", # https://docs.astral.sh/ruff/rules/#flake8-bugbear-b
+ # "A", # https://docs.astral.sh/ruff/rules/#flake8-builtins-a
+]
+ignore = ["RUF005", "RUF012"]
+allowed-confusables = ["𝐚", "𝐀", "∕", "γ", "⨯", "∨"]
+
+[tool.ruff.lint.per-file-ignores]
+"demo/*" = ["D"]
+"doc/*" = ["D"]
+"test/*" = ["D"]
+
+[tool.ruff.lint.pydocstyle]
+convention = "google"
diff --git a/test/conftest.py b/test/conftest.py
index e2c610a6e..5c8030c74 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -8,7 +8,6 @@
class Tester:
-
def assertTrue(self, a):
assert a
diff --git a/test/mockobjects.py b/test/mockobjects.py
index 4c68e0e21..caf8a88a8 100644
--- a/test/mockobjects.py
+++ b/test/mockobjects.py
@@ -2,7 +2,6 @@
class MockMesh:
-
def __init__(self, ufl_id):
self._ufl_id = ufl_id
@@ -12,9 +11,16 @@ def ufl_id(self):
def ufl_domain(self):
return Mesh(triangle, ufl_id=self.ufl_id(), cargo=self)
- def ufl_measure(self, integral_type="dx", subdomain_id="everywhere", metadata=None, subdomain_data=None):
- return Measure(integral_type, subdomain_id=subdomain_id, metadata=metadata, domain=self,
- subdomain_data=subdomain_data)
+ def ufl_measure(
+ self, integral_type="dx", subdomain_id="everywhere", metadata=None, subdomain_data=None
+ ):
+ return Measure(
+ integral_type,
+ subdomain_id=subdomain_id,
+ metadata=metadata,
+ domain=self,
+ subdomain_data=subdomain_data,
+ )
class MockMeshFunction:
@@ -32,5 +38,9 @@ def mesh(self):
def ufl_measure(self, integral_type=None, subdomain_id="everywhere", metadata=None):
return Measure(
- integral_type, subdomain_id=subdomain_id, metadata=metadata,
- domain=self.mesh(), subdomain_data=self)
+ integral_type,
+ subdomain_id=subdomain_id,
+ metadata=metadata,
+ domain=self.mesh(),
+ subdomain_data=self,
+ )
diff --git a/test/test_algorithms.py b/test/test_algorithms.py
index 4b87c2851..f8543249a 100755
--- a/test/test_algorithms.py
+++ b/test/test_algorithms.py
@@ -6,11 +6,37 @@
import pytest
-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 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
@@ -18,29 +44,29 @@
# TODO: add more tests, covering all utility algorithms
-@pytest.fixture(scope='module')
+@pytest.fixture(scope="module")
def element():
return FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
-@pytest.fixture(scope='module')
+@pytest.fixture(scope="module")
def domain():
- return Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ return Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
-@pytest.fixture(scope='module')
+@pytest.fixture(scope="module")
def space(element, domain):
return FunctionSpace(domain, element)
-@pytest.fixture(scope='module')
+@pytest.fixture(scope="module")
def arguments(space):
v = TestFunction(space)
u = TrialFunction(space)
return (v, u)
-@pytest.fixture(scope='module')
+@pytest.fixture(scope="module")
def coefficients(space):
c = Coefficient(space)
f = Coefficient(space)
@@ -110,7 +136,9 @@ def test_expand_indices(domain):
u = TrialFunction(space)
def evaluate(form):
- return form.cell_integral()[0].integrand()((), {v: 3, u: 5}) # TODO: How to define values of derivatives?
+ return form.cell_integral()[0].integrand()(
+ (), {v: 3, u: 5}
+ ) # TODO: How to define values of derivatives?
a = div(grad(v)) * u * dx
# a1 = evaluate(a)
diff --git a/test/test_analyse_demos.py b/test/test_analyse_demos.py
index e9f41451d..a22980136 100755
--- a/test/test_analyse_demos.py
+++ b/test/test_analyse_demos.py
@@ -13,9 +13,8 @@
def get_demo_filenames():
filenames = sorted(
- set(glob(os.path.join(demodir, "*.py")))
- - set(glob(os.path.join(demodir, "_*.py")))
- )
+ set(glob(os.path.join(demodir, "*.py"))) - set(glob(os.path.join(demodir, "_*.py")))
+ )
return filenames
diff --git a/test/test_apply_algebra_lowering.py b/test/test_apply_algebra_lowering.py
index e1f496eb9..c0bdceb46 100755
--- a/test/test_apply_algebra_lowering.py
+++ b/test/test_apply_algebra_lowering.py
@@ -10,51 +10,72 @@
@pytest.fixture
def A0(request):
- return Coefficient(FunctionSpace(
- Mesh(FiniteElement("Lagrange", interval, 1, (1, ), identity_pullback, H1)),
- FiniteElement("Lagrange", interval, 1, (), identity_pullback, H1)))
+ return Coefficient(
+ FunctionSpace(
+ 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(FiniteElement("Lagrange", interval, 1, (1, ), identity_pullback, H1)),
- FiniteElement("Lagrange", interval, 1, (1, 1), identity_pullback, H1)))
+ return Coefficient(
+ FunctionSpace(
+ 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(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)),
- FiniteElement("Lagrange", triangle, 1, (2, 2), identity_pullback, H1)))
+ return Coefficient(
+ FunctionSpace(
+ 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(FiniteElement("Lagrange", triangle, 1, (3, ), identity_pullback, H1)),
- FiniteElement("Lagrange", triangle, 1, (3, 3), identity_pullback, H1)))
+ return Coefficient(
+ FunctionSpace(
+ 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(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)),
- FiniteElement("Lagrange", triangle, 1, (2, 1), identity_pullback, H1)))
+ return Coefficient(
+ FunctionSpace(
+ 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(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)),
- FiniteElement("Lagrange", triangle, 1, (3, 1), identity_pullback, H1)))
+ return Coefficient(
+ FunctionSpace(
+ 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(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)),
- FiniteElement("Lagrange", triangle, 1, (3, 2), identity_pullback, H1)))
+ return Coefficient(
+ FunctionSpace(
+ Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1)),
+ FiniteElement("Lagrange", triangle, 1, (3, 2), identity_pullback, H1),
+ )
+ )
def test_determinant0(A0):
@@ -66,38 +87,42 @@ def test_determinant1(A1):
def test_determinant2(A2):
- assert determinant_expr(A2) == A2[0, 0]*A2[1, 1] - A2[0, 1]*A2[1, 0]
+ assert determinant_expr(A2) == A2[0, 0] * A2[1, 1] - A2[0, 1] * A2[1, 0]
def test_determinant3(A3):
- assert determinant_expr(A3) == (A3[0, 0]*(A3[1, 1]*A3[2, 2] - A3[1, 2]*A3[2, 1])
- + (A3[1, 0]*A3[2, 2] - A3[1, 2]*A3[2, 0])*(-A3[0, 1])
- + A3[0, 2]*(A3[1, 0]*A3[2, 1] - A3[1, 1]*A3[2, 0]))
+ assert determinant_expr(A3) == (
+ A3[0, 0] * (A3[1, 1] * A3[2, 2] - A3[1, 2] * A3[2, 1])
+ + (A3[1, 0] * A3[2, 2] - A3[1, 2] * A3[2, 0]) * (-A3[0, 1])
+ + A3[0, 2] * (A3[1, 0] * A3[2, 1] - A3[1, 1] * A3[2, 0])
+ )
def test_pseudo_determinant21(A21):
i = Index()
- assert renumber_indices(determinant_expr(A21)) == renumber_indices(sqrt(A21[i, 0]*A21[i, 0]))
+ assert renumber_indices(determinant_expr(A21)) == renumber_indices(sqrt(A21[i, 0] * A21[i, 0]))
def test_pseudo_determinant31(A31):
i = Index()
- assert renumber_indices(determinant_expr(A31)) == renumber_indices(sqrt((A31[i, 0]*A31[i, 0])))
+ assert renumber_indices(determinant_expr(A31)) == renumber_indices(
+ sqrt((A31[i, 0] * A31[i, 0]))
+ )
def test_pseudo_determinant32(A32):
i = Index()
c = cross_expr(A32[:, 0], A32[:, 1])
- assert renumber_indices(determinant_expr(A32)) == renumber_indices(sqrt(c[i]*c[i]))
+ assert renumber_indices(determinant_expr(A32)) == renumber_indices(sqrt(c[i] * c[i]))
def test_inverse0(A0):
- expected = 1.0/A0 # stays scalar
+ expected = 1.0 / A0 # stays scalar
assert inverse_expr(A0) == renumber_indices(expected)
def test_inverse1(A1):
- expected = as_tensor(((1.0/A1[0, 0],),)) # reshaped into 1x1 tensor
+ expected = as_tensor(((1.0 / A1[0, 0],),)) # reshaped into 1x1 tensor
assert inverse_expr(A1) == renumber_indices(expected)
diff --git a/test/test_apply_function_pullbacks.py b/test/test_apply_function_pullbacks.py
index 3cb73e898..a3c009a77 100755
--- a/test/test_apply_function_pullbacks.py
+++ b/test/test_apply_function_pullbacks.py
@@ -1,11 +1,17 @@
-import numpy
+import numpy as np
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.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
@@ -13,7 +19,7 @@ def check_single_function_pullback(g, mappings):
expected = mappings[g]
actual = g.ufl_element().pullback.apply(ReferenceValue(g))
assert expected.ufl_shape == actual.ufl_shape
- for idx in numpy.ndindex(actual.ufl_shape):
+ for idx in np.ndindex(actual.ufl_shape):
rexp = renumber_indices(expected[idx])
ract = renumber_indices(actual[idx])
if not rexp == ract:
@@ -26,27 +32,37 @@ def check_single_function_pullback(g, mappings):
print("actual:")
print(str(ract))
print("signatures:")
- print((expected**2*dx).signature())
- print((actual**2*dx).signature())
+ print((expected**2 * dx).signature())
+ print((actual**2 * dx).signature())
print()
assert ract == rexp
def test_apply_single_function_pullbacks_triangle3d():
- triangle3d = Cell("triangle", geometric_dimension=3)
- cell = triangle3d
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (3, ), identity_pullback, H1))
+ cell = Cell("triangle")
+ 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)
+ 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, 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
@@ -123,7 +139,7 @@ def test_apply_single_function_pullbacks_triangle3d():
i, j, k, l = indices(4) # noqa: E741
# Contravariant H(div) Piola mapping:
- M_hdiv = ((1.0/detJ) * J) # Not applying cell orientation here
+ M_hdiv = (1.0 / detJ) * J # Not applying cell orientation here
# Covariant H(curl) Piola mapping: Jinv.T
mappings = {
@@ -131,82 +147,136 @@ def test_apply_single_function_pullbacks_triangle3d():
ul2: rul2 / detJ,
u: ru,
v: rv,
- vd: as_vector(M_hdiv[i, j]*rvd[j], i),
- vc: as_vector(Jinv[j, i]*rvc[j], i),
+ vd: as_vector(M_hdiv[i, j] * rvd[j], i),
+ vc: as_vector(Jinv[j, i] * rvc[j], i),
t: rt,
- s: as_tensor([[rs[0], rs[1], rs[2]],
- [rs[1], rs[3], rs[4]],
- [rs[2], rs[4], rs[5]]]),
+ s: as_tensor([[rs[0], rs[1], rs[2]], [rs[1], rs[3], rs[4]], [rs[2], rs[4], rs[5]]]),
cov2t: as_tensor(Jinv[k, i] * rcov2t[k, l] * Jinv[l, j], (i, j)),
- contra2t: as_tensor((1.0 / detJ)**2
- * J[i, k] * rcontra2t[k, l] * J[j, l], (i, j)),
+ contra2t: as_tensor((1.0 / detJ) ** 2 * J[i, k] * rcontra2t[k, l] * J[j, l], (i, j)),
# Mixed elements become a bit more complicated
uml2: as_vector([ruml2[0] / detJ, ruml2[1] / detJ]),
um: rum,
vm: rvm,
- vdm: as_vector([
- # V
- rvdm[0],
- rvdm[1],
- rvdm[2],
- # Vd
- *(as_tensor(M_hdiv[i, j]*as_vector([rvdm[3], rvdm[4]])[j], (i,))[n]
- for n in range(3))
- ]), vcm: as_vector([
- # Vd
- *(as_tensor(M_hdiv[i, j]*as_vector([rvcm[0], rvcm[1]])[j], (i,))[n]
- for n in range(3)),
- # Vc
- *(as_tensor(Jinv[i, j]*as_vector([rvcm[2], rvcm[3]])[i], (j,))[n]
- for n in range(3))
- ]), tm: as_vector([
- # Vc
- *(as_tensor(Jinv[i, j]*as_vector([rtm[0], rtm[1]])[i], (j,))[n]
- for n in range(3)),
- # T
- rtm[2], rtm[3], rtm[4],
- rtm[5], rtm[6], rtm[7],
- rtm[8], rtm[9], rtm[10],
- ]), sm: as_vector([
- # T
- rsm[0], rsm[1], rsm[2],
- rsm[3], rsm[4], rsm[5],
- rsm[6], rsm[7], rsm[8],
- # S
- rsm[9], rsm[10], rsm[11],
- rsm[10], rsm[12], rsm[13],
- rsm[11], rsm[13], rsm[14],
- ]),
+ vdm: as_vector(
+ [
+ # V
+ rvdm[0],
+ rvdm[1],
+ rvdm[2],
+ # Vd
+ *(
+ as_tensor(M_hdiv[i, j] * as_vector([rvdm[3], rvdm[4]])[j], (i,))[n]
+ for n in range(3)
+ ),
+ ]
+ ),
+ vcm: as_vector(
+ [
+ # Vd
+ *(
+ as_tensor(M_hdiv[i, j] * as_vector([rvcm[0], rvcm[1]])[j], (i,))[n]
+ for n in range(3)
+ ),
+ # Vc
+ *(
+ as_tensor(Jinv[i, j] * as_vector([rvcm[2], rvcm[3]])[i], (j,))[n]
+ for n in range(3)
+ ),
+ ]
+ ),
+ tm: as_vector(
+ [
+ # Vc
+ *(
+ as_tensor(Jinv[i, j] * as_vector([rtm[0], rtm[1]])[i], (j,))[n]
+ for n in range(3)
+ ),
+ # T
+ rtm[2],
+ rtm[3],
+ rtm[4],
+ rtm[5],
+ rtm[6],
+ rtm[7],
+ rtm[8],
+ rtm[9],
+ rtm[10],
+ ]
+ ),
+ sm: as_vector(
+ [
+ # T
+ rsm[0],
+ rsm[1],
+ rsm[2],
+ rsm[3],
+ rsm[4],
+ rsm[5],
+ rsm[6],
+ rsm[7],
+ rsm[8],
+ # S
+ rsm[9],
+ rsm[10],
+ rsm[11],
+ rsm[10],
+ rsm[12],
+ rsm[13],
+ rsm[11],
+ rsm[13],
+ rsm[14],
+ ]
+ ),
# Case from failing ffc demo:
- vd0m: as_vector([
- M_hdiv[0, j]*as_vector([rvd0m[0], rvd0m[1]])[j],
- M_hdiv[1, j]*as_vector([rvd0m[0], rvd0m[1]])[j],
- M_hdiv[2, j]*as_vector([rvd0m[0], rvd0m[1]])[j],
- rvd0m[2]
- ]),
+ vd0m: as_vector(
+ [
+ M_hdiv[0, j] * as_vector([rvd0m[0], rvd0m[1]])[j],
+ M_hdiv[1, j] * as_vector([rvd0m[0], rvd0m[1]])[j],
+ M_hdiv[2, j] * as_vector([rvd0m[0], rvd0m[1]])[j],
+ rvd0m[2],
+ ]
+ ),
# This combines it all:
- w: as_vector([
- # S
- rw[0], rw[1], rw[2],
- rw[1], rw[3], rw[4],
- rw[2], rw[4], rw[5],
- # T
- rw[6], rw[7], rw[8],
- rw[9], rw[10], rw[11],
- rw[12], rw[13], rw[14],
- # Vc
- *(as_tensor(Jinv[i, j]*as_vector([rw[15], rw[16]])[i], (j,))[n]
- for n in range(3)),
- # Vd
- *(as_tensor(M_hdiv[i, j]*as_vector([rw[17], rw[18]])[j], (i,))[n]
- for n in range(3)),
- # V
- rw[19],
- rw[20],
- rw[21],
- # U
- rw[22],
- ]),
+ w: as_vector(
+ [
+ # S
+ rw[0],
+ rw[1],
+ rw[2],
+ rw[1],
+ rw[3],
+ rw[4],
+ rw[2],
+ rw[4],
+ rw[5],
+ # T
+ rw[6],
+ rw[7],
+ rw[8],
+ rw[9],
+ rw[10],
+ rw[11],
+ rw[12],
+ rw[13],
+ rw[14],
+ # Vc
+ *(
+ as_tensor(Jinv[i, j] * as_vector([rw[15], rw[16]])[i], (j,))[n]
+ for n in range(3)
+ ),
+ # Vd
+ *(
+ as_tensor(M_hdiv[i, j] * as_vector([rw[17], rw[18]])[j], (i,))[n]
+ for n in range(3)
+ ),
+ # V
+ rw[19],
+ rw[20],
+ rw[21],
+ # U
+ rw[22],
+ ]
+ ),
}
# Check functions of various elements outside a mixed context
@@ -235,16 +305,18 @@ def test_apply_single_function_pullbacks_triangle3d():
def test_apply_single_function_pullbacks_triangle():
cell = triangle
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1))
+ 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)
+ 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)])
+ 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])
@@ -304,7 +376,7 @@ def test_apply_single_function_pullbacks_triangle():
i, j, k, l = indices(4) # noqa: E741
# Contravariant H(div) Piola mapping:
- M_hdiv = (1.0/detJ) * J
+ M_hdiv = (1.0 / detJ) * J
# Covariant H(curl) Piola mapping: Jinv.T
mappings = {
@@ -312,66 +384,95 @@ def test_apply_single_function_pullbacks_triangle():
ul2: rul2 / detJ,
u: ru,
v: rv,
- vd: as_vector(M_hdiv[i, j]*rvd[j], i),
- vc: as_vector(Jinv[j, i]*rvc[j], i),
+ vd: as_vector(M_hdiv[i, j] * rvd[j], i),
+ vc: as_vector(Jinv[j, i] * rvc[j], i),
t: rt,
s: as_tensor([[rs[0], rs[1]], [rs[1], rs[2]]]),
# Mixed elements become a bit more complicated
uml2: as_vector([ruml2[0] / detJ, ruml2[1] / detJ]),
um: rum,
vm: rvm,
- vdm: as_vector([
- # V
- rvdm[0],
- rvdm[1],
- # Vd
- *(as_tensor(M_hdiv[i, j]*as_vector([rvdm[2], rvdm[3]])[j], (i,))[n]
- for n in range(2)),
- ]),
- vcm: as_vector([
- # Vd
- *(as_tensor(M_hdiv[i, j]*as_vector([rvcm[0], rvcm[1]])[j], (i,))[n]
- for n in range(2)),
- # Vc
- *(as_tensor(Jinv[i, j]*as_vector([rvcm[2], rvcm[3]])[i], (j,))[n]
- for n in range(2)),
- ]),
- tm: as_vector([
- # Vc
- *(as_tensor(Jinv[i, j]*as_vector([rtm[0], rtm[1]])[i], (j,))[n]
- for n in range(2)),
- # T
- rtm[2], rtm[3],
- rtm[4], rtm[5],
- ]),
- sm: as_vector([
- # T
- rsm[0], rsm[1],
- rsm[2], rsm[3],
- # S
- rsm[4], rsm[5],
- rsm[5], rsm[6],
- ]),
+ vdm: as_vector(
+ [
+ # V
+ rvdm[0],
+ rvdm[1],
+ # Vd
+ *(
+ as_tensor(M_hdiv[i, j] * as_vector([rvdm[2], rvdm[3]])[j], (i,))[n]
+ for n in range(2)
+ ),
+ ]
+ ),
+ vcm: as_vector(
+ [
+ # Vd
+ *(
+ as_tensor(M_hdiv[i, j] * as_vector([rvcm[0], rvcm[1]])[j], (i,))[n]
+ for n in range(2)
+ ),
+ # Vc
+ *(
+ as_tensor(Jinv[i, j] * as_vector([rvcm[2], rvcm[3]])[i], (j,))[n]
+ for n in range(2)
+ ),
+ ]
+ ),
+ tm: as_vector(
+ [
+ # Vc
+ *(
+ as_tensor(Jinv[i, j] * as_vector([rtm[0], rtm[1]])[i], (j,))[n]
+ for n in range(2)
+ ),
+ # T
+ rtm[2],
+ rtm[3],
+ rtm[4],
+ rtm[5],
+ ]
+ ),
+ sm: as_vector(
+ [
+ # T
+ rsm[0],
+ rsm[1],
+ rsm[2],
+ rsm[3],
+ # S
+ rsm[4],
+ rsm[5],
+ rsm[5],
+ rsm[6],
+ ]
+ ),
# This combines it all:
- w: as_vector([
- # S
- rw[0], rw[1],
- rw[1], rw[2],
- # T
- rw[3], rw[4],
- rw[5], rw[6],
- # Vc
- *(as_tensor(Jinv[i, j]*as_vector([rw[7], rw[8]])[i], (j,))[n]
- for n in range(2)),
- # Vd
- *(as_tensor(M_hdiv[i, j]*as_vector([rw[9], rw[10]])[j], (i,))[n]
- for n in range(2)),
- # V
- rw[11],
- rw[12],
- # U
- rw[13],
- ]),
+ w: as_vector(
+ [
+ # S
+ rw[0],
+ rw[1],
+ rw[1],
+ rw[2],
+ # T
+ rw[3],
+ rw[4],
+ rw[5],
+ rw[6],
+ # Vc
+ *(as_tensor(Jinv[i, j] * as_vector([rw[7], rw[8]])[i], (j,))[n] for n in range(2)),
+ # Vd
+ *(
+ as_tensor(M_hdiv[i, j] * as_vector([rw[9], rw[10]])[j], (i,))[n]
+ for n in range(2)
+ ),
+ # V
+ rw[11],
+ rw[12],
+ # U
+ rw[13],
+ ]
+ ),
}
# Check functions of various elements outside a mixed context
diff --git a/test/test_apply_restrictions.py b/test/test_apply_restrictions.py
index efb9ad514..61b370e00 100755
--- a/test/test_apply_restrictions.py
+++ b/test/test_apply_restrictions.py
@@ -1,6 +1,16 @@
from pytest import raises
-from ufl import Coefficient, FacetNormal, FunctionSpace, Mesh, SpatialCoordinate, 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
@@ -14,7 +24,7 @@ def test_apply_restrictions():
V1 = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1)
V2 = FiniteElement("Lagrange", cell, 2, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1))
+ 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)
@@ -31,23 +41,26 @@ def test_apply_restrictions():
# Continuous function gets default restriction if none
# provided otherwise the user choice is respected
- assert apply_restrictions(f) == f('+')
- assert apply_restrictions(f('-')) == f('-')
- assert apply_restrictions(f('+')) == f('+')
+ assert apply_restrictions(f) == f("+")
+ assert apply_restrictions(f("-")) == f("-")
+ assert apply_restrictions(f("+")) == f("+")
# Propagation to terminals
- assert apply_restrictions((f + f0)('+')) == f('+') + f0('+')
+ assert apply_restrictions((f + f0)("+")) == f("+") + f0("+")
# Propagation stops at grad
- assert apply_restrictions(grad(f)('-')) == grad(f)('-')
- assert apply_restrictions((grad(f)**2)('+')) == grad(f)('+')**2
- assert apply_restrictions((grad(f) + grad(g))('-')) == (grad(f)('-') + grad(g)('-'))
+ assert apply_restrictions(grad(f)("-")) == grad(f)("-")
+ assert apply_restrictions((grad(f) ** 2)("+")) == grad(f)("+") ** 2
+ assert apply_restrictions((grad(f) + grad(g))("-")) == (grad(f)("-") + grad(g)("-"))
# x is the same from both sides but computed from one of them
- assert apply_default_restrictions(x) == x('+')
+ assert apply_default_restrictions(x) == x("+")
# n on a linear mesh is opposite pointing from the other side
- assert apply_restrictions(n('+')) == n('+')
- assert renumber_indices(apply_restrictions(n('-'))) == renumber_indices(as_tensor(-1*n('+')[i], i))
- # This would be nicer, but -f is translated to -1*f which is translated to as_tensor(-1*f[i], i).
- # assert apply_restrictions(n('-')) == -n('+')
+ assert apply_restrictions(n("+")) == n("+")
+ assert renumber_indices(apply_restrictions(n("-"))) == renumber_indices(
+ as_tensor(-1 * n("+")[i], i)
+ )
+ # This would be nicer, but -f is translated to -1*f which is
+ # translated to as_tensor(-1*f[i], i). assert
+ # apply_restrictions(n('-')) == -n('+')
diff --git a/test/test_arithmetic.py b/test/test_arithmetic.py
index 858f9ddd6..554eca967 100755
--- a/test/test_arithmetic.py
+++ b/test/test_arithmetic.py
@@ -1,5 +1,17 @@
-from ufl import (Identity, Mesh, SpatialCoordinate, 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
@@ -19,13 +31,13 @@ def test_scalar_casting(self):
def test_ufl_float_division(self):
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)
@@ -68,7 +80,7 @@ def test_elem_mult(self):
def test_elem_mult_on_matrices(self):
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
A = as_matrix(((1, 2), (3, 4)))
B = as_matrix(((4, 5), (6, 7)))
@@ -77,7 +89,7 @@ def test_elem_mult_on_matrices(self):
x, y = SpatialCoordinate(domain)
A = as_matrix(((x, y), (3, 4)))
B = as_matrix(((4, 5), (y, x)))
- self.assertEqual(elem_mult(A, B), as_matrix(((4*x, 5*y), (3*y, 4*x))))
+ self.assertEqual(elem_mult(A, B), as_matrix(((4 * x, 5 * y), (3 * y, 4 * x))))
x, y = SpatialCoordinate(domain)
A = as_matrix(((x, y), (3, 4)))
@@ -86,17 +98,18 @@ def test_elem_mult_on_matrices(self):
def test_elem_div(self):
- domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1))
+ 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)))
- self.assertEqual(elem_div(A, B), as_matrix(((x/7, y/8, z/9), (3/z, 4/x, 5/y))))
+ self.assertEqual(elem_div(A, B), as_matrix(((x / 7, y / 8, z / 9), (3 / z, 4 / x, 5 / y))))
def test_elem_op(self):
- domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1))
+ 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)),
- (sin(3), sin(4), sin(5)))))
+ self.assertEqual(
+ elem_op(sin, A), as_matrix(((sin(x), sin(y), sin(z)), (sin(3), sin(4), sin(5))))
+ )
self.assertEqual(elem_op(sin, A).dx(0).ufl_shape, (2, 3))
diff --git a/test/test_automatic_differentiation.py b/test/test_automatic_differentiation.py
index 37bfd02f1..45549ae6b 100755
--- a/test/test_automatic_differentiation.py
+++ b/test/test_automatic_differentiation.py
@@ -7,13 +7,76 @@
import pytest
-from ufl import (And, Argument, CellDiameter, CellVolume, Circumradius, Coefficient, Constant, FacetArea, FacetNormal,
- 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 import (
+ And,
+ Argument,
+ CellDiameter,
+ CellVolume,
+ Circumradius,
+ Coefficient,
+ Constant,
+ FacetArea,
+ FacetNormal,
+ 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
@@ -23,11 +86,10 @@
class ExpressionCollection(object):
-
def __init__(self, cell):
self.cell = cell
- d = cell.geometric_dimension()
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1))
+ d = cell.topological_dimension()
+ domain = Mesh(FiniteElement("Lagrange", cell, 1, (d,), identity_pullback, H1))
x = SpatialCoordinate(domain)
n = FacetNormal(domain)
@@ -49,7 +111,7 @@ def __init__(self, cell):
eps = PermutationSymbol(d)
U = FiniteElement("Undefined", cell, None, (), identity_pullback, L2)
- V = FiniteElement("Undefined", cell, None, (d, ), 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)
@@ -65,6 +127,7 @@ def __init__(self, cell):
class ObjectCollection(object):
pass
+
self.shared_objects = ObjectCollection()
for key, value in list(locals().items()):
setattr(self.shared_objects, key, value)
@@ -78,51 +141,84 @@ class ObjectCollection(object):
self.terminals += self.geometry
self.terminals += self.functions
- self.algebra = ([
- u*2, v*2, w*2,
- u+2*u, v+2*v, w+2*w,
- 2/u, u/2, v/2, w/2,
- u**3, 3**u,
- ])
- self.mathfunctions = ([
- abs(u), sqrt(u), exp(u), ln(u),
- cos(u), sin(u), tan(u), acos(u), asin(u), atan(u),
- erf(u), bessel_I(1, u), bessel_J(1, u), bessel_K(1, u), bessel_Y(1, u),
- ])
- self.variables = ([
- variable(u), variable(v), variable(w),
- variable(w*u), 3*variable(w*u),
- ])
+ self.algebra = [
+ u * 2,
+ v * 2,
+ w * 2,
+ u + 2 * u,
+ v + 2 * v,
+ w + 2 * w,
+ 2 / u,
+ u / 2,
+ v / 2,
+ w / 2,
+ u**3,
+ 3**u,
+ ]
+ self.mathfunctions = [
+ abs(u),
+ sqrt(u),
+ exp(u),
+ ln(u),
+ cos(u),
+ sin(u),
+ tan(u),
+ acos(u),
+ asin(u),
+ atan(u),
+ erf(u),
+ bessel_I(1, u),
+ bessel_J(1, u),
+ bessel_K(1, u),
+ bessel_Y(1, u),
+ ]
+ self.variables = [
+ variable(u),
+ variable(v),
+ variable(w),
+ variable(w * u),
+ 3 * variable(w * u),
+ ]
if d == 1:
w2 = as_matrix(((u**2,),))
if d == 2:
- w2 = as_matrix(((u**2, u**3),
- (u**4, u**5)))
+ w2 = as_matrix(((u**2, u**3), (u**4, u**5)))
if d == 3:
- w2 = as_matrix(((u**2, u**3, u**4),
- (u**4, u**5, u**6),
- (u**6, u**7, u**8)))
+ w2 = as_matrix(((u**2, u**3, u**4), (u**4, u**5, u**6), (u**6, u**7, u**8)))
# Indexed, ListTensor, ComponentTensor, IndexSum
i, j, k, l = indices(4) # noqa: E741
- self.indexing = ([
- v[0], w[d-1, 0], v[i], w[i, j],
- v[:], w[0, :], w[:, 0],
- v[...], w[0, ...], w[..., 0],
- v[i]*v[j], w[i, 0]*v[j], w[d-1, j]*v[i],
- v[i]*v[i], w[i, 0]*w[0, i], v[i]*w[0, i],
- v[j]*w[d-1, j], w[i, i], w[i, j]*w[j, i],
- as_tensor(v[i]*w[k, 0], (k, i)),
- as_tensor(v[i]*w[k, 0], (k, i))[:, l],
- as_tensor(w[i, j]*w[k, l], (k, j, l, i)),
- as_tensor(w[i, j]*w[k, l], (k, j, l, i))[0, 0, 0, 0],
- as_vector((u, 2, 3)),
- as_matrix(((u**2, u**3), (u**4, u**5))),
- as_vector((u, 2, 3))[i],
- w2[i, j]*w[i, j],
- ])
- self.conditionals = ([
+ self.indexing = [
+ v[0],
+ w[d - 1, 0],
+ v[i],
+ w[i, j],
+ v[:],
+ w[0, :],
+ w[:, 0],
+ v[...],
+ w[0, ...],
+ w[..., 0],
+ v[i] * v[j],
+ w[i, 0] * v[j],
+ w[d - 1, j] * v[i],
+ v[i] * v[i],
+ w[i, 0] * w[0, i],
+ v[i] * w[0, i],
+ v[j] * w[d - 1, j],
+ w[i, i],
+ w[i, j] * w[j, i],
+ as_tensor(v[i] * w[k, 0], (k, i)),
+ as_tensor(v[i] * w[k, 0], (k, i))[:, l],
+ as_tensor(w[i, j] * w[k, l], (k, j, l, i)),
+ as_tensor(w[i, j] * w[k, l], (k, j, l, i))[0, 0, 0, 0],
+ as_vector((u, 2, 3)),
+ as_matrix(((u**2, u**3), (u**4, u**5))),
+ as_vector((u, 2, 3))[i],
+ w2[i, j] * w[i, j],
+ ]
+ self.conditionals = [
conditional(le(u, 1.0), 1, 0),
conditional(eq(3.0, u), 1, 0),
conditional(ne(sin(u), cos(u)), 1, 0),
@@ -136,16 +232,16 @@ class ObjectCollection(object):
conditional(Not(ge(u, 0.0)), 1, 2),
conditional(And(Not(ge(u, 0.0)), lt(u, 1.0)), 1, 2),
conditional(le(u, 0.0), u**3, ln(u)),
- ])
- self.restrictions = [u('+'), u('-'), v('+'), v('-'), w('+'), w('-')]
+ ]
+ self.restrictions = [u("+"), u("-"), v("+"), v("-"), w("+"), w("-")]
if d > 1:
i, j = indices(2)
- self.restrictions += ([
- v('+')[i]*v('+')[i],
- v[i]('+')*v[i]('+'),
- (v[i]*v[i])('+'),
- (v[i]*v[j])('+')*w[i, j]('+'),
- ])
+ self.restrictions += [
+ v("+")[i] * v("+")[i],
+ v[i]("+") * v[i]("+"),
+ (v[i] * v[i])("+"),
+ (v[i] * v[j])("+") * w[i, j]("+"),
+ ]
self.noncompounds = []
self.noncompounds += self.algebra
@@ -158,7 +254,7 @@ class ObjectCollection(object):
if d == 1:
self.tensorproducts = []
else:
- self.tensorproducts = ([
+ self.tensorproducts = [
dot(v, v),
dot(v, w),
dot(w, w),
@@ -168,26 +264,32 @@ class ObjectCollection(object):
outer(w, v),
outer(v, w),
outer(w, w),
- ])
+ ]
if d == 1:
self.tensoralgebra = []
else:
- self.tensoralgebra = ([
- w.T, sym(w), skew(w), dev(w),
- det(w), tr(w), cofac(w), inv(w),
- ])
+ self.tensoralgebra = [
+ w.T,
+ sym(w),
+ skew(w),
+ dev(w),
+ det(w),
+ tr(w),
+ cofac(w),
+ inv(w),
+ ]
if d != 3:
self.crossproducts = []
else:
- self.crossproducts = ([
+ self.crossproducts = [
cross(v, v),
- cross(v, 2*v),
+ cross(v, 2 * v),
cross(v, w[0, :]),
cross(v, w[:, 1]),
cross(w[:, 0], v),
- ])
+ ]
self.compounds = []
self.compounds += self.tensorproducts
@@ -220,31 +322,36 @@ def ad_algorithm(expr):
expr,
apply_expand_compounds_before=True,
apply_expand_compounds_after=False,
- use_alternative_wrapper_algorithm=True)
+ use_alternative_wrapper_algorithm=True,
+ )
elif alt == 2:
return expand_derivatives(
expr,
apply_expand_compounds_before=False,
apply_expand_compounds_after=True,
- use_alternative_wrapper_algorithm=False)
+ use_alternative_wrapper_algorithm=False,
+ )
elif alt == 3:
return expand_derivatives(
expr,
apply_expand_compounds_before=False,
apply_expand_compounds_after=False,
- use_alternative_wrapper_algorithm=False)
+ use_alternative_wrapper_algorithm=False,
+ )
elif alt == 4:
return expand_derivatives(
expr,
apply_expand_compounds_before=False,
apply_expand_compounds_after=False,
- use_alternative_wrapper_algorithm=True)
+ use_alternative_wrapper_algorithm=True,
+ )
elif alt == 5:
return expand_derivatives(
expr,
apply_expand_compounds_before=False,
apply_expand_compounds_after=False,
- use_alternative_wrapper_algorithm=False)
+ use_alternative_wrapper_algorithm=False,
+ )
def _test_no_derivatives_no_change(self, collection):
@@ -277,7 +384,9 @@ def test_no_derivatives_no_change(self, d_expr):
_test_no_derivatives_no_change(self, ex.noncompounds)
-def xtest_compounds_no_derivatives_no_change(self, d_expr): # This test fails with expand_compounds enabled
+def xtest_compounds_no_derivatives_no_change(
+ self, d_expr
+): # This test fails with expand_compounds enabled
d, ex = d_expr
_test_no_derivatives_no_change(self, ex.compounds)
@@ -298,13 +407,13 @@ def _test_zero_derivatives_of_terminals_produce_the_right_types_and_shapes(self,
for var in (u, v, w):
before = derivative(t, var) # This will often get preliminary simplified to zero
after = ad_algorithm(before)
- expected = 0*t
+ expected = 0 * t
# print '\n', str(expected), '\n', str(after), '\n', str(before), '\n'
assert after == expected
- before = derivative(c*t, var) # This will usually not get simplified to zero
+ before = derivative(c * t, var) # This will usually not get simplified to zero
after = ad_algorithm(before)
- expected = 0*t
+ expected = 0 * t
# print '\n', str(expected), '\n', str(after), '\n', str(before), '\n'
assert after == expected
@@ -328,13 +437,13 @@ def _test_zero_diffs_of_terminals_produce_the_right_types_and_shapes(self, colle
for var in (vu, vv, vw):
before = diff(t, var) # This will often get preliminary simplified to zero
after = ad_algorithm(before)
- expected = 0*outer(t, var)
+ expected = 0 * outer(t, var)
# print '\n', str(expected), '\n', str(after), '\n', str(before), '\n'
assert after == expected
- before = diff(c*t, var) # This will usually not get simplified to zero
+ before = diff(c * t, var) # This will usually not get simplified to zero
after = ad_algorithm(before)
- expected = 0*outer(t, var)
+ expected = 0 * outer(t, var)
# print '\n', str(expected), '\n', str(after), '\n', str(before), '\n'
assert after == expected
@@ -356,22 +465,22 @@ def _test_zero_derivatives_of_noncompounds_produce_the_right_types_and_shapes(se
for t in collection.noncompounds:
for var in (u, v, w):
if debug:
- print('\n', 'shapes: ', t.ufl_shape, var.ufl_shape, '\n')
+ print("\n", "shapes: ", t.ufl_shape, var.ufl_shape, "\n")
if debug:
- print('\n', 't: ', str(t), '\n')
+ print("\n", "t: ", str(t), "\n")
if debug:
- print('\n', 't ind: ', str(t.ufl_free_indices), '\n')
+ print("\n", "t ind: ", str(t.ufl_free_indices), "\n")
if debug:
- print('\n', 'var: ', str(var), '\n')
+ print("\n", "var: ", str(var), "\n")
before = derivative(t, var)
if debug:
- print('\n', 'before: ', str(before), '\n')
+ print("\n", "before: ", str(before), "\n")
after = ad_algorithm(before)
if debug:
- print('\n', 'after: ', str(after), '\n')
- expected = 0*t
+ print("\n", "after: ", str(after), "\n")
+ expected = 0 * t
if debug:
- print('\n', 'expected: ', str(expected), '\n')
+ print("\n", "expected: ", str(expected), "\n")
assert after == expected
@@ -396,13 +505,13 @@ def _test_zero_diffs_of_noncompounds_produce_the_right_types_and_shapes(self, co
for var in (vu, vv, vw):
before = diff(t, var)
if debug:
- print('\n', 'before: ', str(before), '\n')
+ print("\n", "before: ", str(before), "\n")
after = ad_algorithm(before)
if debug:
- print('\n', 'after: ', str(after), '\n')
- expected = 0*outer(t, var)
+ print("\n", "after: ", str(after), "\n")
+ expected = 0 * outer(t, var)
if debug:
- print('\n', 'expected: ', str(expected), '\n')
+ print("\n", "expected: ", str(expected), "\n")
# print '\n', str(expected), '\n', str(after), '\n', str(before), '\n'
assert after == expected
@@ -429,16 +538,16 @@ def _test_nonzero_derivatives_of_noncompounds_produce_the_right_types_and_shapes
continue
if debug:
- print(('\n', '...: ', t.ufl_shape, var.ufl_shape, '\n'))
+ print(("\n", "...: ", t.ufl_shape, var.ufl_shape, "\n"))
before = derivative(t, var)
if debug:
- print(('\n', 'before: ', str(before), '\n'))
+ print(("\n", "before: ", str(before), "\n"))
after = ad_algorithm(before)
if debug:
- print(('\n', 'after: ', str(after), '\n'))
- expected_shape = 0*t
+ print(("\n", "after: ", str(after), "\n"))
+ expected_shape = 0 * t
if debug:
- print(('\n', 'expected_shape: ', str(expected_shape), '\n'))
+ print(("\n", "expected_shape: ", str(expected_shape), "\n"))
# print '\n', str(expected_shape), '\n', str(after), '\n', str(before), '\n'
if var in unique_post_traversal(t):
@@ -475,13 +584,13 @@ def _test_nonzero_diffs_of_noncompounds_produce_the_right_types_and_shapes(self,
before = diff(t, var)
if debug:
- print(('\n', 'before: ', str(before), '\n'))
+ print(("\n", "before: ", str(before), "\n"))
after = ad_algorithm(before)
if debug:
- print(('\n', 'after: ', str(after), '\n'))
- expected_shape = 0*outer(t, var) # expected shape, not necessarily value
+ print(("\n", "after: ", str(after), "\n"))
+ expected_shape = 0 * outer(t, var) # expected shape, not necessarily value
if debug:
- print(('\n', 'expected_shape: ', str(expected_shape), '\n'))
+ print(("\n", "expected_shape: ", str(expected_shape), "\n"))
# print '\n', str(expected_shape), '\n', str(after), '\n', str(before), '\n'
if var in unique_post_traversal(t):
@@ -502,8 +611,8 @@ def test_grad_coeff(self, d_expr):
after = ad_algorithm(before)
if before.ufl_shape != after.ufl_shape:
- print(('\n', 'shapes:', before.ufl_shape, after.ufl_shape))
- print(('\n', str(before), '\n', str(after), '\n'))
+ print(("\n", "shapes:", before.ufl_shape, after.ufl_shape))
+ print(("\n", str(before), "\n", str(after), "\n"))
self.assertEqualTotalShape(before, after)
if f is u: # Differing by being wrapped in indexing types
@@ -543,8 +652,8 @@ def test_derivative_grad_coeff(self, d_expr):
# assert after == expected
if 0:
print()
- print(('B', f, "::", before))
- print(('A', f, "::", after))
+ print(("B", f, "::", before))
+ print(("A", f, "::", after))
def xtest_derivative_grad_coeff_with_variation_components(self, d_expr):
@@ -556,7 +665,7 @@ def xtest_derivative_grad_coeff_with_variation_components(self, d_expr):
dw = collection.shared_objects.dw
for g, dg in ((v, dv), (w, dw)):
# Pick a single component
- ii = (0,)*(len(g.ufl_shape))
+ ii = (0,) * (len(g.ufl_shape))
f = g[ii]
df = dg[ii]
@@ -576,5 +685,5 @@ def xtest_derivative_grad_coeff_with_variation_components(self, d_expr):
# assert after == expected
if 0:
print()
- print(('B', f, "::", before))
- print(('A', f, "::", after))
+ print(("B", f, "::", before))
+ print(("A", f, "::", after))
diff --git a/test/test_cell.py b/test/test_cell.py
index 5d9472a1d..dc26052a4 100644
--- a/test/test_cell.py
+++ b/test/test_cell.py
@@ -85,4 +85,3 @@ def test_tensorproductcell():
cell = orig.reconstruct()
assert cell.sub_cells() == orig.sub_cells()
assert cell.topological_dimension() == orig.topological_dimension()
- assert cell.geometric_dimension() == orig.geometric_dimension()
diff --git a/test/test_change_to_local.py b/test/test_change_to_local.py
index f0789fbba..973195bc7 100755
--- a/test/test_change_to_local.py
+++ b/test/test_change_to_local.py
@@ -11,15 +11,15 @@
def test_change_to_reference_grad():
cell = triangle
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1))
+ 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))
+ V = FunctionSpace(domain, FiniteElement("Lagrange", cell, 1, (2,), identity_pullback, H1))
u = Coefficient(U)
v = Coefficient(V)
Jinv = JacobianInverse(domain)
i, j, k = indices(3)
q, r, s = indices(3)
- t, = indices(1)
+ (t,) = indices(1)
# Single grad change on a scalar function
expr = grad(u)
@@ -36,27 +36,33 @@ def test_change_to_reference_grad():
# Multiple grads should work fine for affine domains:
expr = grad(grad(u))
actual = change_to_reference_grad(expr)
- expected = as_tensor(
- Jinv[s, j] * (Jinv[r, i] * ReferenceGrad(ReferenceGrad(u))[r, s]), (i, j))
+ expected = as_tensor(Jinv[s, j] * (Jinv[r, i] * ReferenceGrad(ReferenceGrad(u))[r, s]), (i, j))
assert renumber_indices(actual) == renumber_indices(expected)
expr = grad(grad(grad(u)))
actual = change_to_reference_grad(expr)
expected = as_tensor(
- Jinv[s, k] * (Jinv[r, j] * (Jinv[q, i] * ReferenceGrad(ReferenceGrad(ReferenceGrad(u)))[q, r, s])), (i, j, k))
+ Jinv[s, k]
+ * (Jinv[r, j] * (Jinv[q, i] * ReferenceGrad(ReferenceGrad(ReferenceGrad(u)))[q, r, s])),
+ (i, j, k),
+ )
assert renumber_indices(actual) == renumber_indices(expected)
# Multiple grads on a vector valued function
expr = grad(grad(v))
actual = change_to_reference_grad(expr)
expected = as_tensor(
- Jinv[s, j] * (Jinv[r, i] * ReferenceGrad(ReferenceGrad(v))[t, r, s]), (t, i, j))
+ Jinv[s, j] * (Jinv[r, i] * ReferenceGrad(ReferenceGrad(v))[t, r, s]), (t, i, j)
+ )
assert renumber_indices(actual) == renumber_indices(expected)
expr = grad(grad(grad(v)))
actual = change_to_reference_grad(expr)
- expected = as_tensor(Jinv[s, k] * (Jinv[r, j] * (
- Jinv[q, i] * ReferenceGrad(ReferenceGrad(ReferenceGrad(v)))[t, q, r, s])), (t, i, j, k))
+ expected = as_tensor(
+ Jinv[s, k]
+ * (Jinv[r, j] * (Jinv[q, i] * ReferenceGrad(ReferenceGrad(ReferenceGrad(v)))[t, q, r, s])),
+ (t, i, j, k),
+ )
assert renumber_indices(actual) == renumber_indices(expected)
# print tree_format(expected)
diff --git a/test/test_change_to_reference_frame.py b/test/test_change_to_reference_frame.py
index 9b46b510c..63b0751b3 100755
--- a/test/test_change_to_reference_frame.py
+++ b/test/test_change_to_reference_frame.py
@@ -14,10 +14,10 @@ def change_to_reference_frame(expr):
def test_change_unmapped_form_arguments_to_reference_frame():
U = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
- V = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
+ V = FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1)
T = FiniteElement("Lagrange", triangle, 1, (2, 2), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)
@@ -31,9 +31,9 @@ def test_change_unmapped_form_arguments_to_reference_frame():
def test_change_hdiv_form_arguments_to_reference_frame():
- V = FiniteElement("Raviart-Thomas", triangle, 1, (2, ), contravariant_piola, HDiv)
+ V = FiniteElement("Raviart-Thomas", triangle, 1, (2,), contravariant_piola, HDiv)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
v_space = FunctionSpace(domain, V)
expr = Coefficient(v_space)
@@ -41,15 +41,15 @@ def test_change_hdiv_form_arguments_to_reference_frame():
def test_change_hcurl_form_arguments_to_reference_frame():
- V = FiniteElement("Raviart-Thomas", triangle, 1, (2, ), contravariant_piola, HDiv)
+ V = FiniteElement("Raviart-Thomas", triangle, 1, (2,), contravariant_piola, HDiv)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
v_space = FunctionSpace(domain, V)
expr = Coefficient(v_space)
assert change_to_reference_frame(expr) == ReferenceValue(expr)
- '''
+ """
# user input
grad(f + g)('+')
# change to reference frame
@@ -121,10 +121,10 @@ def test_change_hcurl_form_arguments_to_reference_frame():
e = v | cell_avg(v) | facet_avg(v) | at_cell_midpoint(v) | at_facet_midpoint(v)
# evaluated at point or averaged over cell entity
m = e | indexed(e) # scalar component of
- '''
+ """
-'''
+"""
New form preprocessing pipeline:
Preferably introduce these changes:
@@ -139,7 +139,8 @@ def test_change_hcurl_form_arguments_to_reference_frame():
b) lower_compound_operators # expand_compounds
c) change_to_reference_frame # change f->rv(f), m->M*rv(m),
grad(f)->K*rgrad(rv(f)),
- grad(grad(f))->K*rgrad(K*rgrad(rv(f))), grad(expr)->K*rgrad(expr)
+ grad(grad(f))->K*rgrad(K*rgrad(rv(f))),
+ grad(expr)->K*rgrad(expr)
# if grad(expr)->K*rgrad(expr) should be valid,
then rgrad must be applicable to quite generic expressions
d) apply_derivatives # one possibility is to add an apply_mapped_derivatives AD
@@ -147,4 +148,4 @@ def test_change_hcurl_form_arguments_to_reference_frame():
e) apply_geometry_lowering
f) apply_restrictions # requiring grad(f)('+') instead of grad(f('+')) would simplify a lot...
iii) extract final metadata about elements and coefficient ordering
-'''
+"""
diff --git a/test/test_check_arities.py b/test/test_check_arities.py
index 37ce7a26d..e2c32f5f5 100755
--- a/test/test_check_arities.py
+++ b/test/test_check_arities.py
@@ -1,7 +1,23 @@
import pytest
-from ufl import (Coefficient, FacetNormal, FunctionSpace, Mesh, SpatialCoordinate, TestFunction, TrialFunction, 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
@@ -12,8 +28,8 @@
def test_check_arities():
# Code from bitbucket issue #49
cell = tetrahedron
- D = Mesh(FiniteElement("Lagrange", cell, 1, (3, ), identity_pullback, H1))
- V = FunctionSpace(D, FiniteElement("Lagrange", cell, 2, (3, ), identity_pullback, H1))
+ 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)
@@ -36,8 +52,8 @@ def test_check_arities():
def test_complex_arities():
cell = tetrahedron
- D = Mesh(FiniteElement("Lagrange", cell, 1, (3, ), identity_pullback, H1))
- V = FunctionSpace(D, FiniteElement("Lagrange", cell, 2, (3, ), identity_pullback, H1))
+ 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)
@@ -52,3 +68,19 @@ def test_complex_arities():
with pytest.raises(ArityMismatch):
compute_form_data(inner(conj(v), u) * dx, complex_mode=True)
+
+
+def test_product_arity():
+ cell = tetrahedron
+ 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)
+
+ with pytest.raises(ArityMismatch):
+ F = inner(u, u) * dx
+ compute_form_data(F, complex_mode=True)
+
+ with pytest.raises(ArityMismatch):
+ L = inner(v, v) * dx
+ compute_form_data(L, complex_mode=False)
diff --git a/test/test_classcoverage.py b/test/test_classcoverage.py
index 9a61261e3..522e2f999 100755
--- a/test/test_classcoverage.py
+++ b/test/test_classcoverage.py
@@ -2,19 +2,113 @@
__date__ = "2008-09-06 -- 2009-02-10"
import ufl
-from ufl import * # noqa: F403, F401
-from ufl import (And, Argument, CellDiameter, CellVolume, Circumradius, Coefficient, Constant, FacetArea, FacetNormal,
- 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 import * # noqa: F403
+from ufl import (
+ And,
+ Argument,
+ CellDiameter,
+ CellVolume,
+ Circumradius,
+ Coefficient,
+ Constant,
+ FacetArea,
+ FacetNormal,
+ 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
+from ufl.classes import * # noqa: F403
+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
@@ -25,9 +119,9 @@
def _test_object(a, shape, free_indices):
# Check if instances of this type has certain memory consuming members
- if hasattr(a, '_repr'):
+ if hasattr(a, "_repr"):
has_repr.add(a.__class__.__name__)
- if hasattr(a, '__dict__'):
+ if hasattr(a, "__dict__"):
has_dict.add(a.__class__.__name__)
# Test reproduction via repr string
@@ -60,9 +154,9 @@ def _test_object(a, shape, free_indices):
def _test_object2(a):
# Check if instances of this type has certain memory consuming members
- if hasattr(a, '_repr'):
+ if hasattr(a, "_repr"):
has_repr.add(a.__class__.__name__)
- if hasattr(a, '__dict__'):
+ if hasattr(a, "__dict__"):
has_dict.add(a.__class__.__name__)
# Test reproduction via repr string
@@ -92,8 +186,9 @@ def testExports(self):
for c in list(vars(m).values()):
if isinstance(c, type) and issubclass(c, Expr):
all_expr_classes.append(c)
- missing_classes = set(c.__name__ for c in all_expr_classes)\
- - set(c.__name__ for c in all_ufl_classes)
+ missing_classes = set(c.__name__ for c in all_expr_classes) - set(
+ c.__name__ for c in all_ufl_classes
+ )
if missing_classes:
print("The following subclasses of Expr were not exported from ufl.classes:")
print(("\n".join(sorted(missing_classes))))
@@ -101,22 +196,21 @@ def testExports(self):
def testAll(self):
-
Expr.ufl_enable_profiling()
# --- Elements:
cell = triangle
- dim = cell.geometric_dimension()
+ dim = 2
e0 = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1)
- e1 = FiniteElement("Lagrange", cell, 1, (2, ), 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 = FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)
+ e13D = FiniteElement("Lagrange", tetrahedron, 1, (3,), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (dim, ), identity_pullback, H1))
- domain3D = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1))
+ 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)
@@ -136,7 +230,7 @@ def testAll(self):
_test_object(v0, (), ())
_test_object(v1, (dim,), ())
_test_object(v2, (dim, dim), ())
- _test_object(v3, (1 + dim + dim ** 2, ), ())
+ _test_object(v3, (1 + dim + dim**2,), ())
f0 = Coefficient(e0_space)
f1 = Coefficient(e1_space)
@@ -146,7 +240,7 @@ def testAll(self):
_test_object(f0, (), ())
_test_object(f1, (dim,), ())
_test_object(f2, (dim, dim), ())
- _test_object(f3, (1 + dim + dim ** 2, ), ())
+ _test_object(f3, (1 + dim + dim**2,), ())
c = Constant(domain)
_test_object(c, (), ())
@@ -193,11 +287,11 @@ def testAll(self):
_test_object(g, (dim, dim), ())
g = FacetJacobian(domain)
- _test_object(g, (dim, dim-1), ())
+ _test_object(g, (dim, dim - 1), ())
g = FacetJacobianDeterminant(domain)
_test_object(g, (), ())
g = FacetJacobianInverse(domain)
- _test_object(g, (dim-1, dim), ())
+ _test_object(g, (dim - 1, dim), ())
g = FacetNormal(domain)
_test_object(g, (dim,), ())
@@ -229,7 +323,7 @@ def testAll(self):
a = variable(v2)
_test_object(a, (dim, dim), ())
a = variable(v3)
- _test_object(a, (1 + dim + dim ** 2, ), ())
+ _test_object(a, (1 + dim + dim**2,), ())
a = variable(f0)
_test_object(a, (), ())
a = variable(f1)
@@ -237,7 +331,7 @@ def testAll(self):
a = variable(f2)
_test_object(a, (dim, dim), ())
a = variable(f3)
- _test_object(a, (1 + dim + dim ** 2, ), ())
+ _test_object(a, (1 + dim + dim**2,), ())
# a = MultiIndex()
@@ -290,7 +384,7 @@ def testAll(self):
a = v2 + f2 + v2
_test_object(a, (dim, dim), ())
# a = Product()
- a = 3*v0*(2.0*v0)*f0*(v0*3.0)
+ a = 3 * v0 * (2.0 * v0) * f0 * (v0 * 3.0)
_test_object(a, (), ())
# a = Division()
a = v0 / 2.0
@@ -302,78 +396,76 @@ def testAll(self):
# a = Power()
a = f0**3
_test_object(a, (), ())
- a = (f0*2)**1.23
+ a = (f0 * 2) ** 1.23
_test_object(a, (), ())
# a = ListTensor()
- a = as_vector([1.0, 2.0*f0, f0**2])
+ a = as_vector([1.0, 2.0 * f0, f0**2])
_test_object(a, (3,), ())
- a = as_matrix([[1.0, 2.0*f0, f0**2],
- [1.0, 2.0*f0, f0**2]])
+ a = as_matrix([[1.0, 2.0 * f0, f0**2], [1.0, 2.0 * f0, f0**2]])
_test_object(a, (2, 3), ())
- a = as_tensor([[[0.00, 0.01, 0.02],
- [0.10, 0.11, 0.12]],
- [[1.00, 1.01, 1.02],
- [1.10, 1.11, 1.12]]])
+ a = as_tensor(
+ [[[0.00, 0.01, 0.02], [0.10, 0.11, 0.12]], [[1.00, 1.01, 1.02], [1.10, 1.11, 1.12]]]
+ )
_test_object(a, (2, 2, 3), ())
# a = ComponentTensor()
- a = as_vector(v1[i]*f1[j], i)
+ a = as_vector(v1[i] * f1[j], i)
_test_object(a, (dim,), (j,))
- a = as_matrix(v1[i]*f1[j], (j, i))
+ a = as_matrix(v1[i] * f1[j], (j, i))
_test_object(a, (dim, dim), ())
- a = as_tensor(v1[i]*f1[j], (i, j))
+ a = as_tensor(v1[i] * f1[j], (i, j))
_test_object(a, (dim, dim), ())
- a = as_tensor(v2[i, j]*f2[j, k], (i, k))
+ a = as_tensor(v2[i, j] * f2[j, k], (i, k))
_test_object(a, (dim, dim), ())
a = dev(v2)
_test_object(a, (dim, dim), ())
a = dev(f2)
_test_object(a, (dim, dim), ())
- a = dev(f2*f0+v2*3)
+ a = dev(f2 * f0 + v2 * 3)
_test_object(a, (dim, dim), ())
a = sym(v2)
_test_object(a, (dim, dim), ())
a = sym(f2)
_test_object(a, (dim, dim), ())
- a = sym(f2*f0+v2*3)
+ a = sym(f2 * f0 + v2 * 3)
_test_object(a, (dim, dim), ())
a = skew(v2)
_test_object(a, (dim, dim), ())
a = skew(f2)
_test_object(a, (dim, dim), ())
- a = skew(f2*f0+v2*3)
+ a = skew(f2 * f0 + v2 * 3)
_test_object(a, (dim, dim), ())
a = v2.T
_test_object(a, (dim, dim), ())
a = f2.T
_test_object(a, (dim, dim), ())
- a = transpose(f2*f0+v2*3)
+ a = transpose(f2 * f0 + v2 * 3)
_test_object(a, (dim, dim), ())
a = det(v2)
_test_object(a, (), ())
a = det(f2)
_test_object(a, (), ())
- a = det(f2*f0+v2*3)
+ a = det(f2 * f0 + v2 * 3)
_test_object(a, (), ())
a = tr(v2)
_test_object(a, (), ())
a = tr(f2)
_test_object(a, (), ())
- a = tr(f2*f0+v2*3)
+ a = tr(f2 * f0 + v2 * 3)
_test_object(a, (), ())
a = cofac(v2)
_test_object(a, (dim, dim), ())
a = cofac(f2)
_test_object(a, (dim, dim), ())
- a = cofac(f2*f0+v2*3)
+ a = cofac(f2 * f0 + v2 * 3)
_test_object(a, (dim, dim), ())
cond1 = le(f0, 1.0)
@@ -468,7 +560,7 @@ def testAll(self):
s0 = variable(f0)
s1 = variable(f1)
s2 = variable(f2)
- f = dot(s0*s1, s2)
+ f = dot(s0 * s1, s2)
_test_object(s0, (), ())
_test_object(s1, (dim,), ())
_test_object(s2, (dim, dim), ())
@@ -477,7 +569,14 @@ def testAll(self):
a = diff(f, s0)
_test_object(a, (dim,), ())
a = diff(f, s1)
- _test_object(a, (dim, dim,), ())
+ _test_object(
+ a,
+ (
+ dim,
+ dim,
+ ),
+ (),
+ )
a = diff(f, s2)
_test_object(a, (dim, dim, dim), ())
@@ -500,9 +599,9 @@ def testAll(self):
_test_object(a, (dim, dim), ())
a = grad(f1)
_test_object(a, (dim, dim), ())
- a = grad(f0*v0)
+ a = grad(f0 * v0)
_test_object(a, (dim,), ())
- a = grad(f0*v1)
+ a = grad(f0 * v1)
_test_object(a, (dim, dim), ())
a = nabla_div(v1)
@@ -524,9 +623,9 @@ def testAll(self):
_test_object(a, (dim, dim), ())
a = nabla_grad(f1)
_test_object(a, (dim, dim), ())
- a = nabla_grad(f0*v0)
+ a = nabla_grad(f0 * v0)
_test_object(a, (dim,), ())
- a = nabla_grad(f0*v1)
+ a = nabla_grad(f0 * v1)
_test_object(a, (dim, dim), ())
a = curl(v13D)
@@ -540,16 +639,16 @@ def testAll(self):
# a = PositiveRestricted(v0)
# _test_object(a, (), ())
- a = v0('+')
+ a = v0("+")
_test_object(a, (), ())
- a = v0('+')*f0
+ a = v0("+") * f0
_test_object(a, (), ())
# a = NegativeRestricted(v0)
# _test_object(a, (), ())
- a = v0('-')
+ a = v0("-")
_test_object(a, (), ())
- a = v0('-') + f0
+ a = v0("-") + f0
_test_object(a, (), ())
a = cell_avg(v0)
@@ -567,47 +666,47 @@ def testAll(self):
# --- Integrals:
- a = v0*dx
+ a = v0 * dx
_test_form(a)
- a = v0*dx(0)
+ a = v0 * dx(0)
_test_form(a)
- a = v0*dx(1)
+ a = v0 * dx(1)
_test_form(a)
- a = v0*ds
+ a = v0 * ds
_test_form(a)
- a = v0*ds(0)
+ a = v0 * ds(0)
_test_form(a)
- a = v0*ds(1)
+ a = v0 * ds(1)
_test_form(a)
- a = v0*dS
+ a = v0 * dS
_test_form(a)
- a = v0*dS(0)
+ a = v0 * dS(0)
_test_form(a)
- a = v0*dS(1)
+ a = v0 * dS(1)
_test_form(a)
- a = v0*dot(v1, f1)*dx
+ a = v0 * dot(v1, f1) * dx
_test_form(a)
- a = v0*dot(v1, f1)*dx(0)
+ a = v0 * dot(v1, f1) * dx(0)
_test_form(a)
- a = v0*dot(v1, f1)*dx(1)
+ a = v0 * dot(v1, f1) * dx(1)
_test_form(a)
- a = v0*dot(v1, f1)*ds
+ a = v0 * dot(v1, f1) * ds
_test_form(a)
- a = v0*dot(v1, f1)*ds(0)
+ a = v0 * dot(v1, f1) * ds(0)
_test_form(a)
- a = v0*dot(v1, f1)*ds(1)
+ a = v0 * dot(v1, f1) * ds(1)
_test_form(a)
- a = v0*dot(v1, f1)*dS
+ a = v0 * dot(v1, f1) * dS
_test_form(a)
- a = v0*dot(v1, f1)*dS(0)
+ a = v0 * dot(v1, f1) * dS(0)
_test_form(a)
- a = v0*dot(v1, f1)*dS(1)
+ a = v0 * dot(v1, f1) * dS(1)
_test_form(a)
# --- Form transformations:
- a = f0*v0*dx + f0*v0*dot(f1, v1)*dx
+ a = f0 * v0 * dx + f0 * v0 * dot(f1, v1) * dx
# b = lhs(a) # TODO
# c = rhs(a) # TODO
d = derivative(a, f1, v1)
diff --git a/test/test_complex.py b/test/test_complex.py
index 6f3eda840..d91d0911c 100755
--- a/test/test_complex.py
+++ b/test/test_complex.py
@@ -2,9 +2,38 @@
import pytest
-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 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
@@ -18,8 +47,8 @@
def test_conj(self):
- z1 = ComplexValue(1+2j)
- z2 = ComplexValue(1-2j)
+ z1 = ComplexValue(1 + 2j)
+ z2 = ComplexValue(1 - 2j)
assert z1 == Conj(z2)
assert z2 == Conj(z1)
@@ -29,7 +58,7 @@ def test_real(self):
z0 = Zero()
z1 = as_ufl(1.0)
z2 = ComplexValue(1j)
- z3 = ComplexValue(1+1j)
+ z3 = ComplexValue(1 + 1j)
assert Real(z1) == z1
assert Real(z3) == z1
assert Real(z2) == z0
@@ -39,7 +68,7 @@ def test_imag(self):
z0 = Zero()
z1 = as_ufl(1.0)
z2 = as_ufl(1j)
- z3 = ComplexValue(1+1j)
+ z3 = ComplexValue(1 + 1j)
assert Imag(z2) == z1
assert Imag(z3) == z1
@@ -48,8 +77,8 @@ def test_imag(self):
def test_compute_form_adjoint(self):
cell = triangle
- element = FiniteElement('Lagrange', cell, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1))
+ 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)
@@ -62,23 +91,28 @@ def test_compute_form_adjoint(self):
def test_complex_algebra(self):
z1 = ComplexValue(1j)
- z2 = ComplexValue(1+1j)
-
- # Remember that ufl.algebra functions return ComplexValues, but ufl.mathfunctions return complex Python scalar
- # Any operations with a ComplexValue and a complex Python scalar promote to ComplexValue
- assert z1*z2 == ComplexValue(-1+1j)
- assert z2/z1 == ComplexValue(1-1j)
- assert pow(z2, z1) == ComplexValue((1+1j)**1j)
- assert sqrt(z2) * as_ufl(1) == ComplexValue(cmath.sqrt(1+1j))
+ z2 = ComplexValue(1 + 1j)
+
+ # Remember that ufl.algebra functions return ComplexValues, but
+ # ufl.mathfunctions return complex Python scalar
+ # Any operations with a ComplexValue and a complex Python scalar
+ # promote to ComplexValue
+ assert z1 * z2 == ComplexValue(-1 + 1j)
+ assert z2 / z1 == ComplexValue(1 - 1j)
+ assert pow(z2, z1) == ComplexValue((1 + 1j) ** 1j)
+ assert sqrt(z2) * as_ufl(1) == ComplexValue(cmath.sqrt(1 + 1j))
assert (sin(z2) + cosh(z2) - atan(z2)) * z1 == ComplexValue(
- (cmath.sin(1+1j) + cmath.cosh(1+1j) - cmath.atan(1+1j))*1j)
- assert (abs(z2) - ln(z2))/exp(z1) == ComplexValue((abs(1+1j) - cmath.log(1+1j))/cmath.exp(1j))
+ (cmath.sin(1 + 1j) + cmath.cosh(1 + 1j) - cmath.atan(1 + 1j)) * 1j
+ )
+ assert (abs(z2) - ln(z2)) / exp(z1) == ComplexValue(
+ (abs(1 + 1j) - cmath.log(1 + 1j)) / cmath.exp(1j)
+ )
def test_automatic_simplification(self):
cell = triangle
element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1)
- domain = Mesh(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)
@@ -92,7 +126,7 @@ def test_automatic_simplification(self):
def test_apply_algebra_lowering_complex(self):
cell = triangle
element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1)
- domain = Mesh(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)
@@ -115,13 +149,15 @@ def test_apply_algebra_lowering_complex(self):
assert lowered_a == gu[lowered_a_index] * gv[lowered_a_index]
assert lowered_b == gv[lowered_b_index] * conj(gu[lowered_b_index])
assert lowered_c == as_tensor(
- conj(gu[lowered_c_indices[0]]) * gv[lowered_c_indices[1]], (lowered_c_indices[0],) + (lowered_c_indices[1],))
+ conj(gu[lowered_c_indices[0]]) * gv[lowered_c_indices[1]],
+ (lowered_c_indices[0],) + (lowered_c_indices[1],),
+ )
def test_remove_complex_nodes(self):
cell = triangle
element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", cell, 1, (2,), identity_pullback, H1))
space = FunctionSpace(domain, element)
u = TrialFunction(space)
@@ -131,7 +167,7 @@ def test_remove_complex_nodes(self):
a = conj(v)
b = real(u)
c = imag(f)
- d = conj(real(v))*imag(conj(u))
+ d = conj(real(v)) * imag(conj(u))
assert remove_complex_nodes(a) == v
assert remove_complex_nodes(b) == u
@@ -144,7 +180,7 @@ def test_remove_complex_nodes(self):
def test_comparison_checker(self):
cell = triangle
element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", cell, 1, (2,), identity_pullback, H1))
space = FunctionSpace(domain, element)
u = TrialFunction(space)
@@ -172,7 +208,7 @@ def test_comparison_checker(self):
def test_complex_degree_handling(self):
cell = triangle
element = FiniteElement("Lagrange", cell, 3, (), identity_pullback, H1)
- domain = Mesh(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/test/test_conditionals.py b/test/test_conditionals.py
index 7b60054d5..8b7980d2d 100755
--- a/test/test_conditionals.py
+++ b/test/test_conditionals.py
@@ -13,7 +13,7 @@
@pytest.fixture
def f():
element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
space = FunctionSpace(domain, element)
return Coefficient(space)
@@ -21,7 +21,7 @@ def f():
@pytest.fixture
def g():
element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), 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 dbadffef6..6101db9ee 100755
--- a/test/test_degree_estimation.py
+++ b/test/test_degree_estimation.py
@@ -1,8 +1,26 @@
__authors__ = "Martin Sandve Alnæs"
__date__ = "2008-03-12 -- 2009-01-28"
-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 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
@@ -12,10 +30,10 @@
def test_total_degree_estimation():
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)
+ VV = FiniteElement("Lagrange", triangle, 3, (2,), identity_pullback, H1)
VM = MixedElement([V1, V2])
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
v1_space = FunctionSpace(domain, V1)
v2_space = FunctionSpace(domain, V2)
@@ -31,8 +49,8 @@ def test_total_degree_estimation():
x, y = SpatialCoordinate(domain)
assert estimate_total_polynomial_degree(x) == 1
assert estimate_total_polynomial_degree(x * y) == 2
- assert estimate_total_polynomial_degree(x ** 3) == 3
- assert estimate_total_polynomial_degree(x ** 3) == 3
+ assert estimate_total_polynomial_degree(x**3) == 3
+ assert estimate_total_polynomial_degree(x**3) == 3
assert estimate_total_polynomial_degree((x - 1) ** 4) == 4
assert estimate_total_polynomial_degree(vv[0]) == 3
@@ -59,7 +77,7 @@ def test_total_degree_estimation():
assert estimate_total_polynomial_degree(f2 + 3) == 2
assert estimate_total_polynomial_degree(f2 * 3) == 2
- assert estimate_total_polynomial_degree(f2 ** 3) == 6
+ assert estimate_total_polynomial_degree(f2**3) == 6
assert estimate_total_polynomial_degree(f2 / 3) == 2
assert estimate_total_polynomial_degree(f2 / v2) == 4
assert estimate_total_polynomial_degree(f2 / (x - 1)) == 3
@@ -70,15 +88,15 @@ def test_total_degree_estimation():
assert estimate_total_polynomial_degree(f2 * v2.dx(0) * v1.dx(0)) == 2 + 1
assert estimate_total_polynomial_degree(f2) == 2
- assert estimate_total_polynomial_degree(f2 ** 2) == 4
- assert estimate_total_polynomial_degree(f2 ** 3) == 6
- assert estimate_total_polynomial_degree(f2 ** 3 * v1) == 7
- assert estimate_total_polynomial_degree(f2 ** 3 * v1 + f1 * v1) == 7
+ assert estimate_total_polynomial_degree(f2**2) == 4
+ assert estimate_total_polynomial_degree(f2**3) == 6
+ assert estimate_total_polynomial_degree(f2**3 * v1) == 7
+ assert estimate_total_polynomial_degree(f2**3 * v1 + f1 * v1) == 7
# Math functions of constant values are constant values
nx, ny = FacetNormal(domain)
- e = nx ** 2
- for f in [sin, cos, tan, abs, lambda z:z**7]:
+ e = nx**2
+ for f in [sin, cos, tan, abs, lambda z: z**7]:
assert estimate_total_polynomial_degree(f(e)) == 0
# Based on the arbitrary chosen math function heuristics...
@@ -89,7 +107,6 @@ def test_total_degree_estimation():
def test_some_compound_types():
-
# NB! Although some compound types are supported here,
# some derivatives and compounds must be preprocessed
# prior to degree estimation. In generic code, this algorithm
@@ -98,8 +115,8 @@ def test_some_compound_types():
etpd = estimate_total_polynomial_degree
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))
+ 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 63528d97f..cce955a12 100755
--- a/test/test_derivative.py
+++ b/test/test_derivative.py
@@ -3,11 +3,58 @@
from itertools import chain
-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 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
@@ -21,13 +68,14 @@
def assertEqualBySampling(actual, expected):
- ad = compute_form_data(actual*dx)
+ ad = compute_form_data(actual * dx)
a = ad.preprocessed_form.integrals_by_type("cell")[0].integrand()
- bd = compute_form_data(expected*dx)
+ bd = compute_form_data(expected * dx)
b = bd.preprocessed_form.integrals_by_type("cell")[0].integrand()
- assert ([ad.function_replace_map[ac] for ac in ad.reduced_coefficients]
- == [bd.function_replace_map[bc] for bc in bd.reduced_coefficients])
+ assert [ad.function_replace_map[ac] for ac in ad.reduced_coefficients] == [
+ bd.function_replace_map[bc] for bc in bd.reduced_coefficients
+ ]
n = ad.num_coefficients
@@ -45,16 +93,24 @@ def make_value(c):
else:
raise NotImplementedError("Tensor valued expressions not supported here.")
- amapping = dict((c, make_value(c)) for c in chain(ad.original_form.coefficients(), ad.original_form.arguments()))
- bmapping = dict((c, make_value(c)) for c in chain(bd.original_form.coefficients(), bd.original_form.arguments()))
- acell = extract_unique_domain(actual).ufl_cell()
- bcell = extract_unique_domain(expected).ufl_cell()
+ amapping = dict(
+ (c, make_value(c))
+ for c in chain(ad.original_form.coefficients(), ad.original_form.arguments())
+ )
+ bmapping = dict(
+ (c, make_value(c))
+ for c in chain(bd.original_form.coefficients(), bd.original_form.arguments())
+ )
+ adomain = extract_unique_domain(actual)
+ bdomain = extract_unique_domain(expected)
+ acell = adomain.ufl_cell()
+ bcell = bdomain.ufl_cell()
assert acell == bcell
- if acell.geometric_dimension() == 1:
+ if adomain.geometric_dimension() == 1:
x = (0.3,)
- elif acell.geometric_dimension() == 2:
+ elif adomain.geometric_dimension() == 2:
x = (0.3, 0.4)
- elif acell.geometric_dimension() == 3:
+ elif adomain.geometric_dimension() == 3:
x = (0.3, 0.4, 0.5)
av = a(x, amapping)
bv = b(x, bmapping)
@@ -75,7 +131,7 @@ def make_value(c):
def _test(self, f, df):
cell = triangle
element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1)
- domain = Mesh(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)
@@ -95,12 +151,13 @@ def _test(self, f, df):
dfv2 = dfv2(x, mapping)
assert dfv1 == dfv2
- dfv1 = derivative(f(7*w), w, v)
- dfv2 = 7*df(7*w, v)
+ dfv1 = derivative(f(7 * w), w, v)
+ dfv2 = 7 * df(7 * w, v)
dfv1 = dfv1(x, mapping)
dfv2 = dfv2(x, mapping)
assert dfv1 == dfv2
+
# --- Literals
@@ -123,6 +180,7 @@ def df(w, v):
_test(self, f, df)
+
# --- Form arguments
@@ -138,21 +196,27 @@ def df(w, v):
def testArgument(self):
def f(w):
- return TestFunction(FunctionSpace(
- Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)),
- FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)))
+ 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()
+
_test(self, f, df)
+
# --- Geometry
def testSpatialCoordinate(self):
def f(w):
return SpatialCoordinate(
- Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)))[0]
+ Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
+ )[0]
def df(w, v):
return zero()
@@ -163,7 +227,8 @@ def df(w, v):
def testFacetNormal(self):
def f(w):
return FacetNormal(
- Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)))[0]
+ Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
+ )[0]
def df(w, v):
return zero()
@@ -173,8 +238,7 @@ def df(w, v):
def testFacetArea(self):
def f(w):
- return FacetArea(
- Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)))
+ return FacetArea(Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1)))
def df(w, v):
return zero()
@@ -185,7 +249,8 @@ def df(w, v):
def testCellDiameter(self):
def f(w):
return CellDiameter(
- Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)))
+ Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
+ )
def df(w, v):
return zero()
@@ -195,22 +260,26 @@ def df(w, v):
def testCircumradius(self):
def f(w):
- return Circumradius(Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)))
+ return Circumradius(
+ Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
+ )
def df(w, v):
return zero()
+
_test(self, f, df)
def testCellVolume(self):
def f(w):
- return CellVolume(Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)))
+ return CellVolume(Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1)))
def df(w, v):
return zero()
_test(self, f, df)
+
# --- Basic operators
@@ -226,10 +295,10 @@ def df(w, v):
def testProduct(self):
def f(w):
- return 3*w
+ return 3 * w
def df(w, v):
- return 3*v
+ return 3 * v
_test(self, f, df)
@@ -239,7 +308,7 @@ def f(w):
return w**3
def df(w, v):
- return 3*w**2*v
+ return 3 * w**2 * v
_test(self, f, df)
@@ -269,7 +338,7 @@ def f(w):
return exp(w)
def df(w, v):
- return v*exp(w)
+ return v * exp(w)
_test(self, f, df)
@@ -289,7 +358,7 @@ def f(w):
return cos(w)
def df(w, v):
- return -v*sin(w)
+ return -v * sin(w)
_test(self, f, df)
@@ -299,7 +368,7 @@ def f(w):
return sin(w)
def df(w, v):
- return v*cos(w)
+ return v * cos(w)
_test(self, f, df)
@@ -309,27 +378,27 @@ def f(w):
return tan(w)
def df(w, v):
- return v*2.0/(cos(2.0*w) + 1.0)
+ return v * 2.0 / (cos(2.0 * w) + 1.0)
_test(self, f, df)
def testAcos(self):
def f(w):
- return acos(w/1000)
+ return acos(w / 1000)
def df(w, v):
- return -(v/1000)/sqrt(1.0 - (w/1000)**2)
+ return -(v / 1000) / sqrt(1.0 - (w / 1000) ** 2)
_test(self, f, df)
def testAsin(self):
def f(w):
- return asin(w/1000)
+ return asin(w / 1000)
def df(w, v):
- return (v/1000)/sqrt(1.0 - (w/1000)**2)
+ return (v / 1000) / sqrt(1.0 - (w / 1000) ** 2)
_test(self, f, df)
@@ -339,10 +408,11 @@ def f(w):
return atan(w)
def df(w, v):
- return v/(1.0 + w**2)
+ return v / (1.0 + w**2)
_test(self, f, df)
+
# FIXME: Add the new erf and bessel_*
# --- Abs and conditionals
@@ -353,7 +423,7 @@ def f(w):
return abs(w)
def df(w, v):
- return sign(w)*v
+ return sign(w) * v
_test(self, f, df)
@@ -363,14 +433,14 @@ def cond(w):
return lt(w, 1.0)
def f(w):
- return conditional(cond(w), 2*w, 3*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)
+ return conditional(cond(w), 1, 0) * 2 * v + conditional(cond(w), 0, 1) * 3 * v
_test(self, f, df)
+
# --- Tensor algebra basics
@@ -379,28 +449,32 @@ def f(w):
# 3*w + 4*w**2 + 5*w**3
a = as_vector((w, w**2, w**3))
b = as_vector((3, 4, 5))
- i, = indices(1)
- return a[i]*b[i]
+ (i,) = indices(1)
+ return a[i] * b[i]
def df(w, v):
- return 3*v + 4*2*w*v + 5*3*w**2*v
+ return 3 * v + 4 * 2 * w * v + 5 * 3 * w**2 * v
_test(self, f, df)
def testListTensor(self):
v = variable(as_ufl(42))
- f = as_tensor((
- ((0, 0), (0, 0)),
- ((v, 2*v), (0, 0)),
- ((v**2, 1), (2, v/2)),
- ))
+ f = as_tensor(
+ (
+ ((0, 0), (0, 0)),
+ ((v, 2 * v), (0, 0)),
+ ((v**2, 1), (2, v / 2)),
+ )
+ )
assert f.ufl_shape == (3, 2, 2)
- g = as_tensor((
- ((0, 0), (0, 0)),
- ((1, 2), (0, 0)),
- ((84, 0), (0, 0.5)),
- ))
+ g = as_tensor(
+ (
+ ((0, 0), (0, 0)),
+ ((1, 2), (0, 0)),
+ ((84, 0), (0, 0.5)),
+ )
+ )
assert g.ufl_shape == (3, 2, 2)
dfv = diff(f, v)
x = None
@@ -409,40 +483,41 @@ def testListTensor(self):
for c in range(2):
self.assertEqual(dfv[a, b, c](x), g[a, b, c](x))
+
# --- Coefficient and argument input configurations
def test_single_scalar_coefficient_derivative(self):
cell = triangle
V = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1)
- domain = Mesh(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)
- a = 3*u**2
+ a = 3 * u**2
b = derivative(a, u, v)
- self.assertEqualAfterPreprocessing(b, 3*(u*(2*v)))
+ self.assertEqualAfterPreprocessing(b, 3 * (u * (2 * v)))
def test_single_vector_coefficient_derivative(self):
cell = triangle
- V = FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1))
+ 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)
- a = 3*dot(u, u)
+ a = 3 * dot(u, u)
actual = derivative(a, u, v)
- expected = 3*(2*(u[i]*v[i]))
+ expected = 3 * (2 * (u[i] * v[i]))
assertEqualBySampling(actual, expected)
def test_multiple_coefficient_derivative(self):
cell = triangle
V = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1)
- W = FiniteElement("Lagrange", cell, 1, (2, ), 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))
+ 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)
@@ -451,23 +526,23 @@ def test_multiple_coefficient_derivative(self):
v = TestFunction(m_space)
vv, vw = split(v)
- a = sin(uv)*dot(uw, uw)
+ a = sin(uv) * dot(uw, uw)
actual = derivative(a, (uv, uw), split(v))
- expected = cos(uv)*vv * (uw[i]*uw[i]) + (uw[j]*vw[j])*2 * sin(uv)
+ expected = cos(uv) * vv * (uw[i] * uw[i]) + (uw[j] * vw[j]) * 2 * sin(uv)
assertEqualBySampling(actual, expected)
actual = derivative(a, (uv, uw), v)
- expected = cos(uv)*vv * (uw[i]*uw[i]) + (uw[j]*vw[j])*2 * sin(uv)
+ expected = cos(uv) * vv * (uw[i] * uw[i]) + (uw[j] * vw[j]) * 2 * sin(uv)
assertEqualBySampling(actual, expected)
def test_indexed_coefficient_derivative(self):
cell = triangle
- ident = Identity(cell.geometric_dimension())
+ ident = Identity(2)
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))
+ 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)
@@ -475,11 +550,11 @@ def test_indexed_coefficient_derivative(self):
w = dot(u, nabla_grad(u))
# a = dot(w, w)
- a = (u[i]*u[k].dx(i)) * w[k]
+ a = (u[i] * u[k].dx(i)) * w[k]
actual = derivative(a, u[0], v)
- dw = v*u[k].dx(0) + u[i]*ident[0, k]*v.dx(i)
+ dw = v * u[k].dx(0) + u[i] * ident[0, k] * v.dx(i)
expected = 2 * w[k] * dw
assertEqualBySampling(actual, expected)
@@ -489,8 +564,8 @@ def test_multiple_indexed_coefficient_derivative(self):
cell = tetrahedron
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))
+ 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)
@@ -498,8 +573,8 @@ def test_multiple_indexed_coefficient_derivative(self):
v = TestFunction(v2_space)
vu, vw = split(v)
- actual = derivative(cos(u[i]*w[i]), (u[2], w[1]), (vu, vw))
- expected = -sin(u[i]*w[i])*(vu*w[2] + u[1]*vw)
+ actual = derivative(cos(u[i] * w[i]), (u[2], w[1]), (vu, vw))
+ expected = -sin(u[i] * w[i]) * (vu * w[2] + u[1] * vw)
assertEqualBySampling(actual, expected)
@@ -507,9 +582,9 @@ def test_multiple_indexed_coefficient_derivative(self):
def test_segregated_derivative_of_convection(self):
cell = tetrahedron
V = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1)
- W = FiniteElement("Lagrange", cell, 1, (3, ), identity_pullback, H1)
+ W = FiniteElement("Lagrange", cell, 1, (3,), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (3, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", cell, 1, (3,), identity_pullback, H1))
v_space = FunctionSpace(domain, V)
w_space = FunctionSpace(domain, W)
@@ -522,32 +597,33 @@ def test_segregated_derivative_of_convection(self):
Lv = {}
Lvu = {}
- for a in range(cell.geometric_dimension()):
+ for a in range(3):
Lv[a] = derivative(L, v[a], dv)
- for b in range(cell.geometric_dimension()):
+ for b in range(3):
Lvu[a, b] = derivative(Lv[a], u[b], du)
- for a in range(cell.geometric_dimension()):
- for b in range(cell.geometric_dimension()):
- form = Lvu[a, b]*dx
+ for a in range(3):
+ for b in range(3):
+ form = Lvu[a, b] * dx
fd = compute_form_data(form)
pf = fd.preprocessed_form
expand_indices(pf)
k = Index()
- for a in range(cell.geometric_dimension()):
- for b in range(cell.geometric_dimension()):
+ for a in range(3):
+ for b in range(3):
actual = Lvu[a, b]
- expected = du*u[a].dx(b)*dv + u[k]*du.dx(k)*dv
+ expected = du * u[a].dx(b) * dv + u[k] * du.dx(k) * dv
assertEqualBySampling(actual, expected)
+
# --- User provided derivatives of coefficients
def test_coefficient_derivatives(self):
V = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
space = FunctionSpace(domain, V)
dv = TestFunction(space)
@@ -560,21 +636,21 @@ def test_coefficient_derivatives(self):
cd = {f: df, g: dg}
integrand = inner(f, g)
- expected = (df*dv)*g + f*(dg*dv)
+ expected = (df * dv) * g + f * (dg * dv)
- F = integrand*dx
+ F = integrand * dx
J = derivative(F, u, dv, cd)
fd = compute_form_data(J)
actual = fd.preprocessed_form.integrals()[0].integrand()
- assert (actual*dx).signature() == (expected*dx).signature()
+ assert (actual * dx).signature() == (expected * dx).signature()
self.assertEqual(replace(actual, fd.function_replace_map), expected)
def test_vector_coefficient_scalar_derivatives(self):
V = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
- VV = FiniteElement("vector Lagrange", triangle, 1, (2, ), identity_pullback, H1)
+ VV = FiniteElement("vector Lagrange", triangle, 1, (2,), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
v_space = FunctionSpace(domain, V)
vv_space = FunctionSpace(domain, VV)
@@ -589,20 +665,20 @@ def test_vector_coefficient_scalar_derivatives(self):
integrand = inner(f, g)
i0, i1, i2, i3, i4 = [Index(count=c) for c in range(5)]
- expected = as_tensor(df[i1]*dv, (i1,))[i0]*g[i0]
+ expected = as_tensor(df[i1] * dv, (i1,))[i0] * g[i0]
- F = integrand*dx
+ F = integrand * dx
J = derivative(F, u, dv, cd)
fd = compute_form_data(J)
actual = fd.preprocessed_form.integrals()[0].integrand()
- assert (actual*dx).signature() == (expected*dx).signature()
+ assert (actual * dx).signature() == (expected * dx).signature()
def test_vector_coefficient_derivatives(self):
- V = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
+ V = FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1)
VV = FiniteElement("Lagrange", triangle, 1, (2, 2), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
v_space = FunctionSpace(domain, V)
vv_space = FunctionSpace(domain, VV)
@@ -617,21 +693,21 @@ def test_vector_coefficient_derivatives(self):
integrand = inner(f, g)
i0, i1, i2, i3, i4 = [Index(count=c) for c in range(5)]
- expected = as_tensor(df[i2, i1]*dv[i1], (i2,))[i0]*g[i0]
+ expected = as_tensor(df[i2, i1] * dv[i1], (i2,))[i0] * g[i0]
- F = integrand*dx
+ F = integrand * dx
J = derivative(F, u, dv, cd)
fd = compute_form_data(J)
actual = fd.preprocessed_form.integrals()[0].integrand()
- assert (actual*dx).signature() == (expected*dx).signature()
+ assert (actual * dx).signature() == (expected * dx).signature()
# self.assertEqual(replace(actual, fd.function_replace_map), expected)
def test_vector_coefficient_derivatives_of_product(self):
- V = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
+ V = FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1)
VV = FiniteElement("Lagrange", triangle, 1, (2, 2), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
v_space = FunctionSpace(domain, V)
vv_space = FunctionSpace(domain, VV)
@@ -644,13 +720,15 @@ def test_vector_coefficient_derivatives_of_product(self):
u = Coefficient(v_space, count=4)
cd = {f: df, g: dg}
- integrand = f[i]*g[i]
+ integrand = f[i] * g[i]
i0, i1, i2, i3, i4 = [Index(count=c) for c in range(5)]
- expected = as_tensor(df[i2, i1]*dv[i1], (i2,))[i0]*g[i0] +\
- f[i0]*as_tensor(dg[i4, i3]*dv[i3], (i4,))[i0]
+ expected = (
+ as_tensor(df[i2, i1] * dv[i1], (i2,))[i0] * g[i0]
+ + f[i0] * as_tensor(dg[i4, i3] * dv[i3], (i4,))[i0]
+ )
- F = integrand*dx
+ F = integrand * dx
J = derivative(F, u, dv, cd)
fd = compute_form_data(J)
actual = fd.preprocessed_form.integrals()[0].integrand()
@@ -664,13 +742,14 @@ def test_vector_coefficient_derivatives_of_product(self):
# TODO: Add tests covering more cases, in particular mixed stuff
+
# --- Some actual forms
def testHyperElasticity(self):
cell = interval
element = FiniteElement("Lagrange", cell, 2, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (1, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", cell, 1, (1,), identity_pullback, H1))
space = FunctionSpace(domain, element)
w = Coefficient(space)
v = TestFunction(space)
@@ -684,10 +763,10 @@ def testHyperElasticity(self):
E = dw + dw**2 / 2
E = variable(E)
- Q = b*E**2
- psi = K*(exp(Q)-1)
+ Q = b * E**2
+ psi = K * (exp(Q) - 1)
- f = psi*dx
+ f = psi * dx
F = derivative(f, w, v)
J = derivative(F, w, u)
@@ -705,18 +784,18 @@ def testHyperElasticity(self):
# classes = set(c.__class__ for c in post_traversal(f_expression))
- Kv = .2
- bv = .3
- dw = .5
- dv = .7
- du = .11
- E = dw + dw**2 / 2.
- Q = bv*E**2
+ Kv = 0.2
+ bv = 0.3
+ dw = 0.5
+ dv = 0.7
+ du = 0.11
+ E = dw + dw**2 / 2.0
+ Q = bv * E**2
expQ = float(exp(Q))
- psi = Kv*(expQ-1)
+ psi = Kv * (expQ - 1)
fv = psi
- Fv = 2*Kv*bv*E*(1+dw)*expQ*dv
- Jv = 2*Kv*bv*expQ*dv*du*(E + (1+dw)**2*(2*bv*E**2 + 1))
+ Fv = 2 * Kv * bv * E * (1 + dw) * expQ * dv
+ Jv = 2 * Kv * bv * expQ * dv * du * (E + (1 + dw) ** 2 * (2 * bv * E**2 + 1))
def Nv(x, derivatives):
assert derivatives == (0,)
@@ -734,7 +813,7 @@ def Nw(x, derivatives):
fv2 = f_expression((0,), mapping)
self.assertAlmostEqual(fv, fv2)
- v, = form_data_F.original_form.arguments()
+ (v,) = form_data_F.original_form.arguments()
mapping = {K: Kv, b: bv, v: Nv, w: Nw}
Fv2 = F_expression((0,), mapping)
self.assertAlmostEqual(Fv, Fv2)
@@ -749,21 +828,22 @@ def test_mass_derived_from_functional(self):
cell = triangle
V = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", cell, 1, (2,), identity_pullback, H1))
space = FunctionSpace(domain, V)
v = TestFunction(space)
u = TrialFunction(space)
w = Coefficient(space)
- f = (w**2/2)*dx
- L = w*v*dx
- a = u*v*dx # noqa: F841
+ f = (w**2 / 2) * dx
+ L = w * v * dx
+ # a = u*v*dx
F = derivative(f, w, v)
- J1 = derivative(L, w, u) # noqa: F841
- J2 = derivative(F, w, u) # noqa: F841
+ derivative(L, w, u)
+ derivative(F, w, u)
# TODO: assert something
+
# --- Interaction with replace
@@ -771,7 +851,7 @@ def test_derivative_replace_works_together(self):
cell = triangle
V = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", cell, 1, (2,), identity_pullback, H1))
space = FunctionSpace(domain, V)
v = TestFunction(space)
@@ -779,14 +859,14 @@ def test_derivative_replace_works_together(self):
f = Coefficient(space)
g = Coefficient(space)
- M = cos(f)*sin(g)
+ M = cos(f) * sin(g)
F = derivative(M, f, v)
J = derivative(F, f, u)
JR = replace(J, {f: g})
- F2 = -sin(f)*v*sin(g)
- J2 = -cos(f)*u*v*sin(g)
- JR2 = -cos(g)*u*v*sin(g)
+ F2 = -sin(f) * v * sin(g)
+ J2 = -cos(f) * u * v * sin(g)
+ JR2 = -cos(g) * u * v * sin(g)
assertEqualBySampling(F, F2)
assertEqualBySampling(J, J2)
@@ -794,30 +874,29 @@ def test_derivative_replace_works_together(self):
def test_index_simplification_handles_repeated_indices(self):
- mesh = Mesh(FiniteElement("Lagrange", quadrilateral, 1, (2, ), identity_pullback, H1))
+ 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)
- A = as_tensor(K[m, i] * K[n, j] * G[i, j, k, l], (m, n, k, l))
+ i, j, k, L, m, n = indices(6)
+ A = as_tensor(K[m, i] * K[n, j] * G[i, j, k, L], (m, n, k, L))
i, j = indices(2)
# Can't use A[i, i, j, j] because UFL automagically index-sums
# repeated indices in the __getitem__ call.
Adiag = Indexed(A, MultiIndex((i, i, j, j)))
A = as_tensor(Adiag, (i, j))
v = TestFunction(V)
- f = inner(A, v)*dx
+ f = inner(A, v) * dx
fd = compute_form_data(f, do_apply_geometry_lowering=True)
- integral, = fd.preprocessed_form.integrals()
+ (integral,) = fd.preprocessed_form.integrals()
assert integral.integrand().ufl_free_indices == ()
def test_index_simplification_reference_grad(self):
- mesh = Mesh(FiniteElement("Lagrange", quadrilateral, 1, (2, ), identity_pullback, H1))
- i, = indices(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(
- apply_algebra_lowering(A[0])))
+ expr = apply_derivatives(apply_geometry_lowering(apply_algebra_lowering(A[0])))
assert expr == ReferenceGrad(SpatialCoordinate(mesh))[0, 0]
assert expr.ufl_free_indices == ()
assert expr.ufl_shape == ()
@@ -825,27 +904,24 @@ def test_index_simplification_reference_grad(self):
# --- Scratch space
+
def test_foobar(self):
- element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)
-
du = TrialFunction(space)
-
U = Coefficient(space)
def planarGrad(u):
- return as_matrix([[u[0].dx(0), 0, u[0].dx(1)],
- [0, 0, 0],
- [u[1].dx(0), 0, u[1].dx(1)]])
+ return as_matrix([[u[0].dx(0), 0, u[0].dx(1)], [0, 0, 0], [u[1].dx(0), 0, u[1].dx(1)]])
def epsilon(u):
- return 0.5*(planarGrad(u)+planarGrad(u).T)
+ return 0.5 * (planarGrad(u) + planarGrad(u).T)
def NS_a(u, v):
return inner(epsilon(u), epsilon(v))
- L = NS_a(U, v)*dx
- a = derivative(L, U, du) # noqa: F841
+ L = NS_a(U, v) * dx
+ _ = derivative(L, U, du)
# TODO: assert something
diff --git a/test/test_diff.py b/test/test_diff.py
index 5a53e5c8d..5388eab43 100755
--- a/test/test_diff.py
+++ b/test/test_diff.py
@@ -3,8 +3,23 @@
import pytest
-from ufl import (Coefficient, FunctionSpace, Mesh, SpatialCoordinate, 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
@@ -46,6 +61,7 @@ def f(v):
def df(v):
return as_ufl(1)
+
_test(f, df)
@@ -55,6 +71,7 @@ def f(v):
def df(v):
return as_ufl(1)
+
_test(f, df)
@@ -64,15 +81,17 @@ def f(v):
def df(v):
return as_ufl(3)
+
_test(f, df)
def testPower(v):
def f(v):
- return v ** 3
+ return v**3
def df(v):
- return 3 * v ** 2
+ return 3 * v**2
+
_test(f, df)
@@ -82,6 +101,7 @@ def f(v):
def df(v):
return as_ufl(1.0 / 3.0)
+
_test(f, df)
@@ -90,7 +110,8 @@ def f(v):
return 3.0 / v
def df(v):
- return -3.0 / v ** 2
+ return -3.0 / v**2
+
_test(f, df)
@@ -100,6 +121,7 @@ def f(v):
def df(v):
return exp(v)
+
_test(f, df)
@@ -109,6 +131,7 @@ def f(v):
def df(v):
return 1.0 / v
+
_test(f, df)
@@ -118,6 +141,7 @@ def f(v):
def df(v):
return cos(v)
+
_test(f, df)
@@ -127,6 +151,7 @@ def f(v):
def df(v):
return -sin(v)
+
_test(f, df)
@@ -136,8 +161,10 @@ def f(v):
def df(v):
return 2.0 / (cos(2.0 * v) + 1.0)
+
_test(f, df)
+
# TODO: Check the following tests. They run into strange math domain errors.
# def testAsin(v):
# def f(v): return asin(v)
@@ -155,37 +182,39 @@ def f(v):
return atan(v)
def df(v):
- return 1 / (1.0 + v ** 2)
+ return 1 / (1.0 + v**2)
+
_test(f, df)
def testIndexSum(v):
def f(v):
# 3*v + 4*v**2 + 5*v**3
- a = as_vector((v, v ** 2, v ** 3))
+ a = as_vector((v, v**2, v**3))
b = as_vector((3, 4, 5))
- i, = indices(1)
+ (i,) = indices(1)
return a[i] * b[i]
def df(v):
- return 3 + 4 * 2 * v + 5 * 3 * v ** 2
+ return 3 + 4 * 2 * v + 5 * 3 * v**2
+
_test(f, df)
def testCoefficient():
- coord_elem = FiniteElement("Lagrange", triangle, 1, (3, ), identity_pullback, H1)
+ coord_elem = FiniteElement("Lagrange", triangle, 1, (3,), identity_pullback, H1)
mesh = Mesh(coord_elem)
V = FunctionSpace(mesh, FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1))
v = Coefficient(V)
- assert round(expand_derivatives(diff(v, v))-1.0, 7) == 0
+ assert round(expand_derivatives(diff(v, v)) - 1.0, 7) == 0
def testDiffX():
cell = triangle
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", cell, 1, (2,), identity_pullback, H1))
x = SpatialCoordinate(domain)
f = x[0] ** 2 * x[1] ** 2
- i, = indices(1)
+ (i,) = indices(1)
df1 = diff(f, x)
df2 = as_vector(f.dx(i), i)
@@ -199,4 +228,5 @@ def testDiffX():
assert round(df10 - 2 * 2 * 9, 7) == 0
assert round(df11 - 2 * 4 * 3, 7) == 0
+
# TODO: More tests involving wrapper types and indices
diff --git a/test/test_domains.py b/test/test_domains.py
index cb9df4ca0..cf425cb2a 100755
--- a/test/test_domains.py
+++ b/test/test_domains.py
@@ -1,16 +1,33 @@
-
"""Tests of domain language and attaching domains to forms."""
import pytest
from mockobjects import MockMesh
import ufl # noqa: F401
-from ufl import (Cell, Coefficient, Constant, FunctionSpace, Mesh, ds, dS, dx, hexahedron, interval, quadrilateral,
- tetrahedron, triangle)
+from ufl import (
+ Cell,
+ Coefficient,
+ Constant,
+ FunctionSpace,
+ Mesh,
+ TestFunction,
+ TrialFunction,
+ dS,
+ ds,
+ dx,
+ hexahedron,
+ interval,
+ quadrilateral,
+ tetrahedron,
+ triangle,
+)
from ufl.algorithms import compute_form_data
+from ufl.domain import extract_domains
from ufl.finiteelement import FiniteElement
-from ufl.pullback import IdentityPullback # noqa: F401
-from ufl.pullback import identity_pullback
+from ufl.pullback import (
+ IdentityPullback, # noqa: F401
+ identity_pullback,
+)
from ufl.sobolevspace import H1
all_cells = (interval, triangle, tetrahedron, quadrilateral, hexahedron)
@@ -18,14 +35,14 @@
def test_construct_domains_from_cells():
for cell in all_cells:
- d = cell.geometric_dimension()
- Mesh(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1))
+ d = cell.topological_dimension()
+ Mesh(FiniteElement("Lagrange", cell, 1, (d,), identity_pullback, H1))
def test_construct_domains_with_names():
for cell in all_cells:
- d = cell.geometric_dimension()
- e = FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1)
+ d = cell.topological_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)
@@ -36,53 +53,60 @@ 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(FiniteElement("Lagrange", cell, 1, (cell.geometric_dimension(), ),
- identity_pullback, H1),
- ufl_id=hash(cell.cellname()))
- for cell in all_cells]
- 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(),
- D.ufl_cell(),
- D.ufl_id()))
+ domains1 = [
+ Mesh(
+ FiniteElement(
+ "Lagrange", cell, 1, (cell.topological_dimension(),), identity_pullback, H1
+ ),
+ ufl_id=hash(cell.cellname()),
+ )
+ for cell in all_cells
+ ]
+ domains2 = [
+ Mesh(
+ FiniteElement(
+ "Lagrange", cell, 1, (cell.topological_dimension(),), identity_pullback, H1
+ ),
+ ufl_id=hash(cell.cellname()),
+ )
+ for cell in sorted(all_cells)
+ ]
+ sdomains = sorted(domains1, key=lambda D: (D.topological_dimension(), D.ufl_cell(), D.ufl_id()))
assert sdomains != domains1
assert sdomains == domains2
def test_topdomain_creation():
- D = Mesh(FiniteElement("Lagrange", interval, 1, (1, ), identity_pullback, H1))
+ D = Mesh(FiniteElement("Lagrange", interval, 1, (1,), identity_pullback, H1))
assert D.geometric_dimension() == 1
- D = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ D = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
assert D.geometric_dimension() == 2
- D = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1))
+ 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(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ D = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
V = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
f = Coefficient(FunctionSpace(D, V))
- assert f.ufl_domains() == (D, )
+ assert f.ufl_domains() == (D,)
M = f * dx
- assert M.ufl_domains() == (D, )
+ assert M.ufl_domains() == (D,)
def test_simple_domain_case():
# Creating domain from just cell with label like new dolfin will do
- D = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1), ufl_id=3)
+ D = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1), ufl_id=3)
V = FunctionSpace(D, FiniteElement("Lagrange", D.ufl_cell(), 1, (), identity_pullback, "H1"))
f = Coefficient(V)
- assert f.ufl_domains() == (D, )
+ assert f.ufl_domains() == (D,)
M = f * dx
- assert M.ufl_domains() == (D, )
+ assert M.ufl_domains() == (D,)
def test_creating_domains_with_coordinate_fields(): # FIXME: Rewrite for new approach
@@ -90,7 +114,7 @@ def test_creating_domains_with_coordinate_fields(): # FIXME: Rewrite for new ap
# Mesh with P2 representation of coordinates
cell = triangle
- P2 = FiniteElement("Lagrange", cell, 2, (2, ), identity_pullback, H1)
+ P2 = FiniteElement("Lagrange", cell, 2, (2,), identity_pullback, H1)
domain = Mesh(P2)
# Piecewise linear function space over quadratic mesh
@@ -99,8 +123,8 @@ def test_creating_domains_with_coordinate_fields(): # FIXME: Rewrite for new ap
f = Coefficient(V)
M = f * dx
- assert f.ufl_domains() == (domain, )
- assert M.ufl_domains() == (domain, )
+ assert f.ufl_domains() == (domain,)
+ assert M.ufl_domains() == (domain,)
# Test the gymnastics that dolfin will have to go through
domain2 = Mesh(P2, ufl_id=domain.ufl_id())
@@ -113,86 +137,193 @@ def test_creating_domains_with_coordinate_fields(): # FIXME: Rewrite for new ap
def test_join_domains():
from ufl.domain import join_domains
+
mesh7 = MockMesh(7)
mesh8 = MockMesh(8)
- triangle3 = Cell("triangle", geometric_dimension=3)
- xa = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
- xb = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
+ triangle = Cell("triangle")
+ 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(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(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(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(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 coordinates require labeling
- 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)))
+ 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(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)]))
+ 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(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)),
- Mesh(FiniteElement("Lagrange", triangle3, 1, (3, ), identity_pullback, H1))])
+ join_domains(
+ [
+ Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1)),
+ Mesh(FiniteElement("Lagrange", triangle, 1, (3,), identity_pullback, H1)),
+ ]
+ )
with pytest.raises(BaseException):
- 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)])
+ join_domains(
+ [
+ Mesh(
+ FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1),
+ ufl_id=7,
+ cargo=mesh7,
+ ),
+ Mesh(
+ FiniteElement("Lagrange", triangle, 1, (3,), identity_pullback, H1),
+ ufl_id=8,
+ cargo=mesh8,
+ ),
+ ]
+ )
# Cargo and mesh ids must match
with pytest.raises(BaseException):
- Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1), 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(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)])
+ 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", triangle, 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(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ D = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
V = FunctionSpace(D, FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1))
f = Coefficient(V)
a = f * dx
- ida, = compute_form_data(a).integral_data
+ (ida,) = compute_form_data(a).integral_data
# Check some integral data
assert ida.integral_type == "cell"
@@ -208,7 +339,7 @@ def test_everywhere_integrals_with_backwards_compatibility():
def test_merge_sort_integral_data():
- D = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ D = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
V = FunctionSpace(D, FiniteElement("CG", triangle, 1, (), identity_pullback, H1))
@@ -246,3 +377,35 @@ def test_merge_sort_integral_data():
assert form_data[4].subdomain_id[0] == 2
assert form_data[4].subdomain_id[1] == 4
assert form_data[4].metadata == {}
+
+
+def test_extract_domains():
+ "Test that the domains are extracted properly from a mixed-domain expression"
+
+ # Create domains of different topological dimensions
+ gdim = 2
+ cell_type_0 = triangle
+ cell_type_1 = interval
+ dom_0 = Mesh(FiniteElement("Lagrange", cell_type_0, 1, (gdim,), identity_pullback, H1))
+ dom_1 = Mesh(FiniteElement("Lagrange", cell_type_1, 1, (gdim,), identity_pullback, H1))
+
+ # Define some finite element spaces
+ k = 1
+ ele_type = "Lagrange"
+ ele_0 = FiniteElement(ele_type, cell_type_0, k, (), identity_pullback, H1)
+ ele_1 = FiniteElement(ele_type, cell_type_1, k, (), identity_pullback, H1)
+
+ V_0 = FunctionSpace(dom_0, ele_0)
+ V_1 = FunctionSpace(dom_1, ele_1)
+
+ # Create test and trial functions
+ u = TrialFunction(V_0)
+ v = TestFunction(V_1)
+
+ # Create a mixed-domain expression
+ expr = u * v
+
+ domains = extract_domains(expr)
+
+ assert domains[0] == dom_1
+ assert domains[1] == dom_0
diff --git a/test/test_duals.py b/test/test_duals.py
index 53e230f3f..909538d49 100644
--- a/test/test_duals.py
+++ b/test/test_duals.py
@@ -3,9 +3,29 @@
import pytest
-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 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
@@ -17,9 +37,9 @@
def test_mixed_functionspace(self):
# Domains
- 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))
+ 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("Lagrange", interval, 1, (), identity_pullback, H1)
f_2d = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
@@ -50,7 +70,7 @@ def test_mixed_functionspace(self):
def test_dual_coefficients():
- domain_2d = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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()
@@ -73,7 +93,7 @@ def test_dual_coefficients():
def test_dual_arguments():
- domain_2d = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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()
@@ -96,11 +116,14 @@ def test_dual_arguments():
def test_addition():
- domain_2d = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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()
+ fvector_2d = FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1)
+ W = FunctionSpace(domain_2d, fvector_2d)
+
u = TrialFunction(V)
v = TestFunction(V)
@@ -132,9 +155,14 @@ def test_addition():
res -= ZeroBaseForm((v,))
assert res == L
+ # Simplification with respect to ufl.Zero
+ a_W = Matrix(W, W)
+ res = a_W + Zero(W.value_shape)
+ assert res == a_W
+
def test_scalar_mult():
- domain_2d = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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()
@@ -152,7 +180,7 @@ def test_scalar_mult():
def test_adjoint():
- domain_2d = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)
@@ -171,10 +199,10 @@ def test_adjoint():
def test_action():
- domain_2d = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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(FiniteElement("Lagrange", interval, 1, (1, ), identity_pullback, H1))
+ 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)
@@ -222,7 +250,8 @@ def test_action():
u2 = Coefficient(U)
u3 = Coefficient(U)
# Check Action left-distributivity with Sum
- # Add 3 Coefficients to check composition of Sum works fine since u + u2 + u3 => Sum(u, Sum(u2, u3))
+ # Add 3 Coefficients to check composition of Sum works fine since u
+ # + u2 + u3 => Sum(u, Sum(u2, u3))
res = action(a, u + u2 + u3)
assert res == Action(a, u3) + Action(a, u) + Action(a, u2)
# Check Action right-distributivity with Sum
@@ -231,10 +260,10 @@ def test_action():
def test_differentiation():
- domain_2d = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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(FiniteElement("Lagrange", interval, 1, (1, ), identity_pullback, H1))
+ 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)
@@ -246,7 +275,10 @@ def test_differentiation():
w = Cofunction(U.dual())
dwdu = expand_derivatives(derivative(w, u))
assert isinstance(dwdu, ZeroBaseForm)
- assert dwdu.arguments() == (Argument(w.ufl_function_space().dual(), 0), Argument(u.ufl_function_space(), 1))
+ assert dwdu.arguments() == (
+ Argument(w.ufl_function_space().dual(), 0),
+ Argument(u.ufl_function_space(), 1),
+ )
# Check compatibility with int/float
assert dwdu == 0
@@ -277,8 +309,10 @@ def test_differentiation():
# -- Action -- #
Ac = Action(w, u)
dAcdu = derivative(Ac, u)
- assert dAcdu == (action(adjoint(derivative(w, u), derivatives_expanded=True), u, derivatives_expanded=True)
- + action(w, derivative(u, u), derivatives_expanded=True))
+ assert dAcdu == (
+ action(adjoint(derivative(w, u), derivatives_expanded=True), u, derivatives_expanded=True)
+ + action(w, derivative(u, u), derivatives_expanded=True)
+ )
dAcdu = expand_derivatives(dAcdu)
# Since dw/du = 0
@@ -294,7 +328,7 @@ def test_differentiation():
def test_zero_base_form_mult():
- domain_2d = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)
@@ -308,7 +342,7 @@ def test_zero_base_form_mult():
def test_base_form_call():
- domain_2d = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)
diff --git a/test/test_equals.py b/test/test_equals.py
index d1251acb8..b34a3c4de 100755
--- a/test/test_equals.py
+++ b/test/test_equals.py
@@ -1,10 +1,10 @@
"""Test of expression comparison."""
from ufl import Coefficient, Cofunction, FunctionSpace, Mesh, triangle
+from ufl.exprcontainers import ExprList
from ufl.finiteelement import FiniteElement
from ufl.pullback import identity_pullback
from ufl.sobolevspace import H1
-from ufl.exprcontainers import ExprList
def test_comparison_of_coefficients():
@@ -12,7 +12,7 @@ def test_comparison_of_coefficients():
U = FiniteElement("Lagrange", triangle, 2, (), identity_pullback, H1)
Ub = FiniteElement("Lagrange", triangle, 2, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)
@@ -44,7 +44,7 @@ def test_comparison_of_cofunctions():
U = FiniteElement("Lagrange", triangle, 2, (), identity_pullback, H1)
Ub = FiniteElement("Lagrange", triangle, 2, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)
@@ -77,7 +77,7 @@ def test_comparison_of_cofunctions():
def test_comparison_of_products():
V = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), 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)
@@ -91,7 +91,7 @@ def test_comparison_of_products():
def test_comparison_of_sums():
V = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), 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)
@@ -105,7 +105,7 @@ def test_comparison_of_sums():
def test_comparison_of_deeply_nested_expression():
V = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), 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)
@@ -118,8 +118,9 @@ def build_expr(a):
elif i % 3 == 1:
a = a * i
elif i % 3 == 2:
- a = a ** i
+ a = a**i
return a
+
a = build_expr(u)
b = build_expr(v)
c = build_expr(w)
diff --git a/test/test_evaluate.py b/test/test_evaluate.py
index 964740aac..da01d452b 100755
--- a/test/test_evaluate.py
+++ b/test/test_evaluate.py
@@ -3,9 +3,36 @@
import math
-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 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
@@ -28,7 +55,7 @@ def testZero():
def testIdentity():
cell = triangle
- ident = Identity(cell.geometric_dimension())
+ ident = Identity(cell.topological_dimension())
s = 123 * ident[0, 0]
e = s((5, 7))
@@ -43,7 +70,7 @@ def testIdentity():
def testCoords():
cell = triangle
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", cell, 1, (2,), identity_pullback, H1))
x = SpatialCoordinate(domain)
s = x[0] + x[1]
e = s((5, 7))
@@ -54,7 +81,7 @@ def testCoords():
def testFunction1():
cell = triangle
element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", cell, 1, (2,), identity_pullback, H1))
space = FunctionSpace(domain, element)
f = Coefficient(space)
s = 3 * f
@@ -66,12 +93,13 @@ def testFunction1():
def testFunction2():
cell = triangle
element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", cell, 1, (2,), identity_pullback, H1))
space = FunctionSpace(domain, element)
f = Coefficient(space)
def g(x):
return x[0]
+
s = 3 * f
e = s((5, 7), {f: g})
v = 3 * 5
@@ -81,12 +109,13 @@ def g(x):
def testArgument2():
cell = triangle
element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", cell, 1, (2,), identity_pullback, H1))
space = FunctionSpace(domain, element)
f = Argument(space, 2)
def g(x):
return x[0]
+
s = 3 * f
e = s((5, 7), {f: g})
v = 3 * 5
@@ -95,40 +124,40 @@ def g(x):
def testAlgebra():
cell = triangle
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1))
+ 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))
- v = 3 * (5. + 7.) - 7 + 5. ** (7. / 2)
+ v = 3 * (5.0 + 7.0) - 7 + 5.0 ** (7.0 / 2)
assert e == v
def testIndexSum():
cell = triangle
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", cell, 1, (2,), identity_pullback, H1))
x = SpatialCoordinate(domain)
- i, = indices(1)
+ (i,) = indices(1)
s = x[i] * x[i]
e = s((5, 7))
- v = 5 ** 2 + 7 ** 2
+ v = 5**2 + 7**2
assert e == v
def testIndexSum2():
cell = triangle
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", cell, 1, (2,), identity_pullback, H1))
x = SpatialCoordinate(domain)
- ident = Identity(cell.geometric_dimension())
+ ident = Identity(cell.topological_dimension())
i, j = indices(2)
s = (x[i] * x[j]) * ident[i, j]
e = s((5, 7))
# v = sum_i sum_j x_i x_j delta_ij = x_0 x_0 + x_1 x_1
- v = 5 ** 2 + 7 ** 2
+ v = 5**2 + 7**2
assert e == v
def testMathFunctions():
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
x = SpatialCoordinate(domain)[0]
s = sin(x)
@@ -163,7 +192,7 @@ def testMathFunctions():
def testListTensor():
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
x, y = SpatialCoordinate(domain)
m = as_matrix([[x, y], [-y, -x]])
@@ -175,12 +204,12 @@ def testListTensor():
s = m[0, 0] * m[1, 0] * m[0, 1] * m[1, 1]
e = s((5, 7))
- v = 5 ** 2 * 7 ** 2
+ v = 5**2 * 7**2
assert e == v
def testComponentTensor1():
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
x = SpatialCoordinate(domain)
m = as_vector(x[i], i)
@@ -191,7 +220,7 @@ def testComponentTensor1():
def testComponentTensor2():
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
x = SpatialCoordinate(domain)
xx = outer(x, x)
@@ -204,7 +233,7 @@ def testComponentTensor2():
def testComponentTensor3():
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
x = SpatialCoordinate(domain)
xx = outer(x, x)
@@ -218,19 +247,20 @@ def testComponentTensor3():
def testCoefficient():
V = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
space = FunctionSpace(domain, V)
f = Coefficient(space)
- e = f ** 2
+ e = f**2
def eval_f(x):
return x[0] * x[1] ** 2
- assert e((3, 7), {f: eval_f}) == (3 * 7 ** 2) ** 2
+
+ assert e((3, 7), {f: eval_f}) == (3 * 7**2) ** 2
def testCoefficientDerivative():
V = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), 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
@@ -239,19 +269,20 @@ def eval_f(x, derivatives):
if not derivatives:
return eval_f.c * x[0] * x[1] ** 2
# assume only first order derivative
- d, = derivatives
+ (d,) = derivatives
if d == 0:
return eval_f.c * x[1] ** 2
if d == 1:
return eval_f.c * x[0] * 2 * x[1]
+
# shows how to attach data to eval_f
eval_f.c = 5
- assert e((3, 7), {f: eval_f}) == (5 * 7 ** 2) ** 2 + (5 * 3 * 2 * 7) ** 2
+ assert e((3, 7), {f: eval_f}) == (5 * 7**2) ** 2 + (5 * 3 * 2 * 7) ** 2
def test_dot():
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
x = SpatialCoordinate(domain)
s = dot(x, 2 * x)
e = s((5, 7))
@@ -260,7 +291,7 @@ def test_dot():
def test_inner():
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)
@@ -270,35 +301,27 @@ def test_inner():
def test_outer():
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)
e = s((5, 7))
- v = 2 * (5 ** 2 + 7 ** 2) ** 2 * (2 ** 2 + 3 ** 2)
+ v = 2 * (5**2 + 7**2) ** 2 * (2**2 + 3**2)
assert e == v
def test_cross():
- domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3,), identity_pullback, H1))
x = SpatialCoordinate(domain)
xv = (3, 5, 7)
# Test cross product of triplets of orthogonal
# vectors, where |a x b| = |a| |b|
ts = [
- [as_vector((x[0], 0, 0)),
- as_vector((0, x[1], 0)),
- as_vector((0, 0, x[2]))],
- [as_vector((x[0], x[1], 0)),
- as_vector((x[1], -x[0], 0)),
- as_vector((0, 0, x[2]))],
- [as_vector((0, x[0], x[1])),
- as_vector((0, x[1], -x[0])),
- as_vector((x[2], 0, 0))],
- [as_vector((x[0], 0, x[1])),
- as_vector((x[1], 0, -x[0])),
- as_vector((0, x[2], 0))],
+ [as_vector((x[0], 0, 0)), as_vector((0, x[1], 0)), as_vector((0, 0, x[2]))],
+ [as_vector((x[0], x[1], 0)), as_vector((x[1], -x[0], 0)), as_vector((0, 0, x[2]))],
+ [as_vector((0, x[0], x[1])), as_vector((0, x[1], -x[0])), as_vector((x[2], 0, 0))],
+ [as_vector((x[0], 0, x[1])), as_vector((x[1], 0, -x[0])), as_vector((0, x[2], 0))],
]
for t in ts:
for a in range(3):
@@ -313,7 +336,7 @@ def test_cross():
def xtest_dev():
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
x = SpatialCoordinate(domain)
xv = (5, 7)
xx = outer(x, x)
@@ -325,31 +348,31 @@ def xtest_dev():
def test_skew():
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
x = SpatialCoordinate(domain)
xv = (5, 7)
xx = outer(x, x)
s1 = skew(2 * xx)
- s2 = (xx - xx.T)
+ s2 = xx - xx.T
e = inner(s1, s1)(xv)
v = inner(s2, s2)(xv)
assert e == v
def test_sym():
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
x = SpatialCoordinate(domain)
xv = (5, 7)
xx = outer(x, x)
s1 = sym(2 * xx)
- s2 = (xx + xx.T)
+ s2 = xx + xx.T
e = inner(s1, s1)(xv)
v = inner(s2, s2)(xv)
assert e == v
def test_tr():
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
x = SpatialCoordinate(domain)
xv = (5, 7)
xx = outer(x, x)
@@ -360,14 +383,14 @@ def test_tr():
def test_det2D():
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
x = SpatialCoordinate(domain)
xv = (5, 7)
a, b = 6.5, -4
xx = as_matrix(((x[0], x[1]), (a, b)))
s = det(2 * xx)
e = s(xv)
- v = 2 ** 2 * (5 * b - 7 * a)
+ v = 2**2 * (5 * b - 7 * a)
assert e == v
@@ -376,14 +399,10 @@ def xtest_det3D(): # FIXME
xv = (5, 7, 9)
a, b, c = 6.5, -4, 3
d, e, f = 2, 3, 4
- xx = as_matrix(((x[0], x[1], x[2]),
- (a, b, c),
- (d, e, f)))
+ xx = as_matrix(((x[0], x[1], x[2]), (a, b, c), (d, e, f)))
s = det(2 * xx)
e = s(xv)
- v = 2 ** 3 * \
- (xv[0] * (b * f - e * c) - xv[1] *
- (a * f - c * d) + xv[2] * (a * e - b * d))
+ v = 2**3 * (xv[0] * (b * f - e * c) - xv[1] * (a * f - c * d) + xv[2] * (a * e - b * d))
assert e == v
diff --git a/test/test_expand_indices.py b/test/test_expand_indices.py
index f8314df2c..1cd9d8983 100755
--- a/test/test_expand_indices.py
+++ b/test/test_expand_indices.py
@@ -8,8 +8,31 @@
import pytest
-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 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
@@ -21,13 +44,12 @@
class Fixture:
-
def __init__(self):
cell = triangle
element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1)
- velement = FiniteElement("Lagrange", cell, 1, (2, ), 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))
+ domain = Mesh(FiniteElement("Lagrange", cell, 1, (2,), identity_pullback, H1))
space = FunctionSpace(domain, element)
vspace = FunctionSpace(domain, velement)
tspace = FunctionSpace(domain, telement)
@@ -113,22 +135,22 @@ def TF(x, derivatives=()):
def compare(self, f, value):
debug = 0
if debug:
- print(('f', f))
+ print(("f", f))
g = expand_derivatives(f)
if debug:
- print(('g', g))
+ print(("g", g))
gv = g(self.x, self.mapping)
assert abs(gv - value) < 1e-7
g = expand_indices(g)
if debug:
- print(('g', g))
+ print(("g", g))
gv = g(self.x, self.mapping)
assert abs(gv - value) < 1e-7
g = renumber_indices(g)
if debug:
- print(('g', g))
+ print(("g", g))
gv = g(self.x, self.mapping)
assert abs(gv - value) < 1e-7
@@ -149,13 +171,13 @@ def test_basic_expand_indices(self, fixt):
compare(sf, 3)
compare(sf + 1, 4)
compare(sf - 2.5, 0.5)
- compare(sf/2, 1.5)
- compare(sf/0.5, 6)
+ compare(sf / 2, 1.5)
+ compare(sf / 0.5, 6)
compare(sf**2, 9)
compare(sf**0.5, 3**0.5)
compare(sf**3, 27)
compare(0.5**sf, 0.5**3)
- compare(sf * (sf/6), 1.5)
+ compare(sf * (sf / 6), 1.5)
compare(sin(sf), math.sin(3))
compare(cos(sf), math.cos(3))
compare(exp(sf), math.exp(3))
@@ -165,13 +187,13 @@ def test_basic_expand_indices(self, fixt):
compare(vf[0], 5)
compare(vf[0] + 1, 6)
compare(vf[0] - 2.5, 2.5)
- compare(vf[0]/2, 2.5)
- compare(vf[0]/0.5, 10)
- compare(vf[0]**2, 25)
- compare(vf[0]**0.5, 5**0.5)
- compare(vf[0]**3, 125)
- compare(0.5**vf[0], 0.5**5)
- compare(vf[0] * (vf[0]/6), 5*(5./6))
+ compare(vf[0] / 2, 2.5)
+ compare(vf[0] / 0.5, 10)
+ compare(vf[0] ** 2, 25)
+ compare(vf[0] ** 0.5, 5**0.5)
+ compare(vf[0] ** 3, 125)
+ compare(0.5 ** vf[0], 0.5**5)
+ compare(vf[0] * (vf[0] / 6), 5 * (5.0 / 6))
compare(sin(vf[0]), math.sin(5))
compare(cos(vf[0]), math.cos(5))
compare(exp(vf[0]), math.exp(5))
@@ -181,13 +203,13 @@ def test_basic_expand_indices(self, fixt):
compare(tf[1, 1], 19)
compare(tf[1, 1] + 1, 20)
compare(tf[1, 1] - 2.5, 16.5)
- compare(tf[1, 1]/2, 9.5)
- compare(tf[1, 1]/0.5, 38)
- compare(tf[1, 1]**2, 19**2)
- compare(tf[1, 1]**0.5, 19**0.5)
- compare(tf[1, 1]**3, 19**3)
- compare(0.5**tf[1, 1], 0.5**19)
- compare(tf[1, 1] * (tf[1, 1]/6), 19*(19./6))
+ compare(tf[1, 1] / 2, 9.5)
+ compare(tf[1, 1] / 0.5, 38)
+ compare(tf[1, 1] ** 2, 19**2)
+ compare(tf[1, 1] ** 0.5, 19**0.5)
+ compare(tf[1, 1] ** 3, 19**3)
+ compare(0.5 ** tf[1, 1], 0.5**19)
+ compare(tf[1, 1] * (tf[1, 1] / 6), 19 * (19.0 / 6))
compare(sin(tf[1, 1]), math.sin(19))
compare(cos(tf[1, 1]), math.cos(19))
compare(exp(tf[1, 1]), math.exp(19))
@@ -200,11 +222,14 @@ def test_expand_indices_index_sum(self, fixt):
compare = fixt.compare
# Basic index sums
- compare(vf[i]*vf[i], 5*5+7*7)
- compare(vf[j]*tf[i, j]*vf[i], 5*5*11 + 5*7*13 + 5*7*17 + 7*7*19)
- compare(vf[j]*tf.T[j, i]*vf[i], 5*5*11 + 5*7*13 + 5*7*17 + 7*7*19)
+ compare(vf[i] * vf[i], 5 * 5 + 7 * 7)
+ compare(vf[j] * tf[i, j] * vf[i], 5 * 5 * 11 + 5 * 7 * 13 + 5 * 7 * 17 + 7 * 7 * 19)
+ compare(vf[j] * tf.T[j, i] * vf[i], 5 * 5 * 11 + 5 * 7 * 13 + 5 * 7 * 17 + 7 * 7 * 19)
compare(tf[i, i], 11 + 19)
- compare(tf[i, j]*(tf[j, i]+outer(vf, vf)[i, j]), (5*5+11)*11 + (7*5+17)*13 + (7*5+13)*17 + (7*7+19)*19)
+ compare(
+ tf[i, j] * (tf[j, i] + outer(vf, vf)[i, j]),
+ (5 * 5 + 11) * 11 + (7 * 5 + 17) * 13 + (7 * 5 + 13) * 17 + (7 * 7 + 19) * 19,
+ )
compare(as_tensor(as_tensor(tf[i, j], (i, j))[k, l], (l, k))[i, i], 11 + 19)
@@ -216,8 +241,8 @@ def test_expand_indices_derivatives(self, fixt):
# Basic derivatives
compare(sf.dx(0), 0.3)
compare(sf.dx(1), 0.31)
- compare(sf.dx(i)*vf[i], 0.30*5 + 0.31*7)
- compare(vf[j].dx(i)*vf[i].dx(j), 0.50*0.50 + 0.51*0.70 + 0.70*0.51 + 0.71*0.71)
+ compare(sf.dx(i) * vf[i], 0.30 * 5 + 0.31 * 7)
+ compare(vf[j].dx(i) * vf[i].dx(j), 0.50 * 0.50 + 0.51 * 0.70 + 0.70 * 0.51 + 0.71 * 0.71)
def test_expand_indices_hyperelasticity(self, fixt):
@@ -240,25 +265,25 @@ def test_expand_indices_hyperelasticity(self, fixt):
compare(F[1, 1], F11)
J = det(F)
- compare(J, (1 + 0.50)*(1 + 0.71) - 0.70*0.51)
+ compare(J, (1 + 0.50) * (1 + 0.71) - 0.70 * 0.51)
# Strain tensors
- C = F.T*F
+ C = F.T * F
# Cij = sum_k Fki Fkj
- C00 = F00*F00 + F10*F10
- C01 = F00*F01 + F10*F11
- C10 = F01*F00 + F11*F10
- C11 = F01*F01 + F11*F11
+ C00 = F00 * F00 + F10 * F10
+ C01 = F00 * F01 + F10 * F11
+ C10 = F01 * F00 + F11 * F10
+ C11 = F01 * F01 + F11 * F11
compare(C[0, 0], C00)
compare(C[0, 1], C01)
compare(C[1, 0], C10)
compare(C[1, 1], C11)
- E = (C-ident)/2
- E00 = (C00-1)/2
- E01 = (C01)/2
- E10 = (C10)/2
- E11 = (C11-1)/2
+ E = (C - ident) / 2
+ E00 = (C00 - 1) / 2
+ E01 = (C01) / 2
+ E10 = (C10) / 2
+ E11 = (C11 - 1) / 2
compare(E[0, 0], E00)
compare(E[0, 1], E01)
compare(E[1, 0], E10)
@@ -270,8 +295,8 @@ def test_expand_indices_hyperelasticity(self, fixt):
compare(Q, Qvalue)
K = 0.5
- psi = (K/2)*exp(Q)
- compare(psi, 0.25*math.exp(Qvalue))
+ psi = (K / 2) * exp(Q)
+ compare(psi, 0.25 * math.exp(Qvalue))
def test_expand_indices_div_grad(self, fixt):
@@ -291,18 +316,21 @@ def test_expand_indices_div_grad(self, fixt):
Dvf = grad(vf)
Lvf = div(Dvf)
Lvf2 = dot(Lvf, Lvf)
- pp = compute_form_data(Lvf2*dx).preprocessed_form.integrals()[0].integrand()
- print(('vf', vf.ufl_shape, str(vf)))
- print(('Dvf', Dvf.ufl_shape, str(Dvf)))
- print(('Lvf', Lvf.ufl_shape, str(Lvf)))
- print(('Lvf2', Lvf2.ufl_shape, str(Lvf2)))
- print(('pp', pp.ufl_shape, str(pp)))
+ pp = compute_form_data(Lvf2 * dx).preprocessed_form.integrals()[0].integrand()
+ print(("vf", vf.ufl_shape, str(vf)))
+ print(("Dvf", Dvf.ufl_shape, str(Dvf)))
+ print(("Lvf", Lvf.ufl_shape, str(Lvf)))
+ print(("Lvf2", Lvf2.ufl_shape, str(Lvf2)))
+ print(("pp", pp.ufl_shape, str(pp)))
a = div(grad(vf))
- compare(dot(a, a), (0.20+0.40)**2 + (0.21+0.41)**2)
+ compare(dot(a, a), (0.20 + 0.40) ** 2 + (0.21 + 0.41) ** 2)
a = div(grad(tf))
- compare(inner(a, a), (10.00+11.00)**2 + (10.01+11.01)**2 + (10.10+11.10)**2 + (10.11+11.11)**2)
+ compare(
+ inner(a, a),
+ (10.00 + 11.00) ** 2 + (10.01 + 11.01) ** 2 + (10.10 + 11.10) ** 2 + (10.11 + 11.11) ** 2,
+ )
def test_expand_indices_nabla_div_grad(self, fixt):
@@ -319,7 +347,10 @@ def test_expand_indices_nabla_div_grad(self, fixt):
compare(a, 3.300 + 3.311)
a = nabla_div(nabla_grad(vf))
- compare(dot(a, a), (0.20+0.40)**2 + (0.21+0.41)**2)
+ compare(dot(a, a), (0.20 + 0.40) ** 2 + (0.21 + 0.41) ** 2)
a = nabla_div(nabla_grad(tf))
- compare(inner(a, a), (10.00+11.00)**2 + (10.01+11.01)**2 + (10.10+11.10)**2 + (10.11+11.11)**2)
+ compare(
+ inner(a, a),
+ (10.00 + 11.00) ** 2 + (10.01 + 11.01) ** 2 + (10.10 + 11.10) ** 2 + (10.11 + 11.11) ** 2,
+ )
diff --git a/test/test_external_operator.py b/test/test_external_operator.py
index 0cdebb422..ab996cce1 100644
--- a/test/test_external_operator.py
+++ b/test/test_external_operator.py
@@ -5,8 +5,28 @@
import pytest
-from ufl import (Action, Argument, Coefficient, Constant, Form, FunctionSpace, Mesh, TestFunction, TrialFunction,
- action, adjoint, cos, derivative, dx, inner, sin, triangle)
+from ufl import (
+ Action,
+ Argument,
+ Coargument,
+ Coefficient,
+ Constant,
+ Form,
+ FunctionSpace,
+ Matrix,
+ Mesh,
+ TestFunction,
+ TrialFunction,
+ action,
+ adjoint,
+ cos,
+ derivative,
+ dx,
+ inner,
+ replace,
+ sin,
+ triangle,
+)
from ufl.algorithms import expand_derivatives
from ufl.algorithms.apply_derivatives import apply_derivatives
from ufl.core.external_operator import ExternalOperator
@@ -18,7 +38,7 @@
@pytest.fixture
def domain_2d():
- return Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ return Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
@pytest.fixture
@@ -62,13 +82,15 @@ def test_properties(V1):
v1 = Argument(V1, 1)
e = ExternalOperator(u, function_space=V1)
- assert str(e) == 'e(w_0; v_0)'
+ assert str(e) == "e(w_0; v_0)"
e = ExternalOperator(u, function_space=V1, derivatives=(1,))
- assert str(e) == '∂e(w_0; v_0)/∂o1'
+ assert str(e) == "∂e(w_0; v_0)/∂o1"
- e = ExternalOperator(u, r, 2 * s, t, function_space=V1, derivatives=(1, 0, 1, 2), argument_slots=(v0, v1))
- assert str(e) == '∂e(w_0, w_1, 2 * w_2, w_3; v_1, v_0)/∂o1∂o3∂o4∂o4'
+ e = ExternalOperator(
+ u, r, 2 * s, t, function_space=V1, derivatives=(1, 0, 1, 2), argument_slots=(v0, v1)
+ )
+ assert str(e) == "∂e(w_0, w_1, 2 * w_2, w_3; v_1, v_0)/∂o1∂o3∂o4∂o4"
def test_form(V1, V2):
@@ -82,7 +104,7 @@ def test_form(V1, V2):
F = N * v * dx
actual = derivative(F, u, u_hat)
- vstar, = N.arguments()
+ (vstar,) = N.arguments()
Nhat = TrialFunction(N.ufl_function_space())
dNdu = N._ufl_expr_reconstruct_(u, m, derivatives=(1, 0), argument_slots=(vstar, u_hat))
@@ -96,7 +118,7 @@ def test_form(V1, V2):
F = N * u * v * dx
actual = derivative(F, u, u_hat)
- vstar, = N.arguments()
+ (vstar,) = N.arguments()
Nhat = TrialFunction(N.ufl_function_space())
dNdu = N._ufl_expr_reconstruct_(u, m, derivatives=(1, 0), argument_slots=(vstar, u_hat))
@@ -126,8 +148,8 @@ def test_differentiation_procedure_action(V1, V2):
assert N2.coefficients() == (s,)
# Get v*
- vstar_N1, = N1.arguments()
- vstar_N2, = N2.arguments()
+ (vstar_N1,) = N1.arguments()
+ (vstar_N2,) = N2.arguments()
assert vstar_N1.ufl_function_space().dual() == V1
assert vstar_N2.ufl_function_space().dual() == V1
@@ -177,18 +199,23 @@ def test_differentiation_procedure_action(V1, V2):
# Check argument slots
assert dN1du.argument_slots() == (vstar_dN1du, u_hat)
- assert dN2du.argument_slots() == (vstar_dN2du, - sin(s) * s_hat)
+ assert dN2du.argument_slots() == (vstar_dN2du, -sin(s) * s_hat)
def test_extractions(domain_2d, V1):
- from ufl.algorithms.analysis import (extract_arguments, extract_arguments_and_coefficients,
- extract_base_form_operators, extract_coefficients, extract_constants)
+ from ufl.algorithms.analysis import (
+ extract_arguments,
+ extract_arguments_and_coefficients,
+ extract_base_form_operators,
+ extract_coefficients,
+ extract_constants,
+ )
u = Coefficient(V1)
c = Constant(domain_2d)
e = ExternalOperator(u, c, function_space=V1)
- vstar_e, = e.arguments()
+ (vstar_e,) = e.arguments()
assert extract_coefficients(e) == [u]
assert extract_arguments(e) == [vstar_e]
@@ -221,7 +248,7 @@ def test_extractions(domain_2d, V1):
w = Coefficient(V1)
e2 = ExternalOperator(w, e, function_space=V1)
- vstar_e2, = e2.arguments()
+ (vstar_e2,) = e2.arguments()
assert extract_coefficients(e2) == [u, w]
assert extract_arguments(e2) == [vstar_e2, u_hat]
@@ -242,24 +269,22 @@ def get_external_operators(form_base):
elif isinstance(form_base, BaseForm):
return form_base.base_form_operators()
else:
- raise ValueError('Expecting FormBase argument!')
+ raise ValueError("Expecting BaseForm argument!")
def test_adjoint_action_jacobian(V1, V2, V3):
-
u = Coefficient(V1)
m = Coefficient(V2)
# N(u, m; v*)
N = ExternalOperator(u, m, function_space=V3)
- vstar_N, = N.arguments()
# Arguments for the Gateaux-derivative
def u_hat(number):
- return Argument(V1, number) # V1: degree 1 # dFdu.arguments()[-1]
+ return Argument(V1, number) # V1: degree 1 # dFdu.arguments()[-1]
def m_hat(number):
- return Argument(V2, number) # V2: degree 2 # dFdm.arguments()[-1]
+ return Argument(V2, number) # V2: degree 2 # dFdm.arguments()[-1]
def vstar_N(number):
return Argument(V3.dual(), number) # V3: degree 3
@@ -273,7 +298,6 @@ def vstar_N(number):
form_base_expressions = (N * dx, N * v2 * dx, N * v3 * dx) # , N)
for F in form_base_expressions:
-
# Get test function
v_F = F.arguments() if isinstance(F, Form) else ()
# If we have a 0-form with an ExternalOperator: e.g. F = N * dx
@@ -318,20 +342,20 @@ def vstar_N(number):
dFdu_adj = adjoint(dFdu)
dFdm_adj = adjoint(dFdm)
- assert dFdu_adj.arguments() == (u_hat(n_arg),) + v_F
- assert dFdm_adj.arguments() == (m_hat(n_arg),) + v_F
+ V = v_F[0].ufl_function_space()
+ assert dFdu_adj.arguments() == (TestFunction(V1), TrialFunction(V))
+ assert dFdm_adj.arguments() == (TestFunction(V2), TrialFunction(V))
# Action of the adjoint
- q = Coefficient(v_F[0].ufl_function_space())
+ q = Coefficient(V)
action_dFdu_adj = action(dFdu_adj, q)
action_dFdm_adj = action(dFdm_adj, q)
- assert action_dFdu_adj.arguments() == (u_hat(n_arg),)
- assert action_dFdm_adj.arguments() == (m_hat(n_arg),)
+ assert action_dFdu_adj.arguments() == (TestFunction(V1),)
+ assert action_dFdm_adj.arguments() == (TestFunction(V2),)
def test_multiple_external_operators(V1, V2):
-
u = Coefficient(V1)
m = Coefficient(V1)
w = Coefficient(V2)
@@ -364,14 +388,18 @@ def test_multiple_external_operators(V1, V2):
dFdN1 = inner(v_hat, v) * dx
dFdN2 = inner(w_hat, v) * dx
dFdN3 = inner(v_hat, v) * dx
- dN1du = N1._ufl_expr_reconstruct_(u, m, derivatives=(1, 0), argument_slots=N1.arguments() + (v_hat,))
+ dN1du = N1._ufl_expr_reconstruct_(
+ u, m, derivatives=(1, 0), argument_slots=N1.arguments() + (v_hat,)
+ )
dN3du = N3._ufl_expr_reconstruct_(u, derivatives=(1,), argument_slots=N3.arguments() + (v_hat,))
assert dFdu == Action(dFdN1, dN1du) + Action(dFdN3, dN3du)
# dFdm = Action(dFdN1, dN1dm)
dFdm = expand_derivatives(derivative(F, m))
- dN1dm = N1._ufl_expr_reconstruct_(u, m, derivatives=(0, 1), argument_slots=N1.arguments() + (v_hat,))
+ dN1dm = N1._ufl_expr_reconstruct_(
+ u, m, derivatives=(0, 1), argument_slots=N1.arguments() + (v_hat,)
+ )
assert dFdm == Action(dFdN1, dN1dm)
@@ -397,23 +425,31 @@ def test_multiple_external_operators(V1, V2):
# Action(∂F/∂N4, Action(∂N4/∂N1, dN1/du))
dFdu = expand_derivatives(derivative(F, u))
dFdN4_partial = inner(v_hat, v) * dx
- dN4dN1_partial = N4._ufl_expr_reconstruct_(N1, u, derivatives=(1, 0), argument_slots=N4.arguments() + (v_hat,))
- dN4du_partial = N4._ufl_expr_reconstruct_(N1, u, derivatives=(0, 1), argument_slots=N4.arguments() + (v_hat,))
+ dN4dN1_partial = N4._ufl_expr_reconstruct_(
+ N1, u, derivatives=(1, 0), argument_slots=N4.arguments() + (v_hat,)
+ )
+ dN4du_partial = N4._ufl_expr_reconstruct_(
+ N1, u, derivatives=(0, 1), argument_slots=N4.arguments() + (v_hat,)
+ )
- assert dFdu == Action(dFdN4_partial, Action(dN4dN1_partial, dN1du)) + Action(dFdN4_partial, dN4du_partial)
+ assert dFdu == Action(dFdN4_partial, Action(dN4dN1_partial, dN1du)) + Action(
+ dFdN4_partial, dN4du_partial
+ )
# dFdm = Action(∂F/∂N4, Action(∂N4/∂N1, dN1/dm))
dFdm = expand_derivatives(derivative(F, m))
assert dFdm == Action(dFdN4_partial, Action(dN4dN1_partial, dN1dm))
- # --- F = < N1(u, m; v*), v > + + + < N4(N1(u, m), u; v*), v > --- #
+ # --- F = < N1(u, m; v*), v > + + + <
+ # N4(N1(u, m), u; v*), v > --- #
F = (inner(N1, v) + inner(N2, v) + inner(N3, v) + inner(N4, v)) * dx
dFdu = expand_derivatives(derivative(F, u))
assert dFdu == Action(dFdN1, dN1du) + Action(dFdN3, dN3du) + Action(
- dFdN4_partial, Action(dN4dN1_partial, dN1du)) + Action(dFdN4_partial, dN4du_partial)
+ dFdN4_partial, Action(dN4dN1_partial, dN1du)
+ ) + Action(dFdN4_partial, dN4du_partial)
dFdm = expand_derivatives(derivative(F, m))
assert dFdm == Action(dFdN1, dN1dm) + Action(dFdN4_partial, Action(dN4dN1_partial, dN1dm))
@@ -421,7 +457,8 @@ def test_multiple_external_operators(V1, V2):
dFdw = expand_derivatives(derivative(F, w))
assert dFdw == Action(dFdN2, dN2dw)
- # --- F = < N5(N4(N1(u, m), u), u; v*), v > + < N1(u, m; v*), v > + < u * N5(N4(N1(u, m), u), u; v*), v >--- #
+ # --- F = < N5(N4(N1(u, m), u), u; v*), v > + < N1(u, m; v*), v > +
+ # < u * N5(N4(N1(u, m), u), u; v*), v >--- #
F = (inner(N5, v) + inner(N1, v) + inner(u * N5, v)) * dx
@@ -439,10 +476,43 @@ def test_multiple_external_operators(V1, V2):
dFdu_partial = inner(w * N5, v) * dx
dFdN1_partial = inner(w, v) * dx
dFdN5_partial = (inner(w, v) + inner(u * w, v)) * dx
- dN5dN4_partial = N5._ufl_expr_reconstruct_(N4, u, derivatives=(1, 0), argument_slots=N4.arguments() + (w,))
- dN5du_partial = N5._ufl_expr_reconstruct_(N4, u, derivatives=(0, 1), argument_slots=N4.arguments() + (w,))
- dN5du = Action(dN5dN4_partial, Action(dN4dN1_partial, dN1du)) + Action(
- dN5dN4_partial, dN4du_partial) + dN5du_partial
+ dN5dN4_partial = N5._ufl_expr_reconstruct_(
+ N4, u, derivatives=(1, 0), argument_slots=N4.arguments() + (w,)
+ )
+ dN5du_partial = N5._ufl_expr_reconstruct_(
+ N4, u, derivatives=(0, 1), argument_slots=N4.arguments() + (w,)
+ )
+ dN5du = (
+ Action(dN5dN4_partial, Action(dN4dN1_partial, dN1du))
+ + Action(dN5dN4_partial, dN4du_partial)
+ + dN5du_partial
+ )
dFdu = expand_derivatives(derivative(F, u))
assert dFdu == dFdu_partial + Action(dFdN1_partial, dN1du) + Action(dFdN5_partial, dN5du)
+
+
+def test_replace(V1):
+ u = Coefficient(V1, count=0)
+ N = ExternalOperator(u, function_space=V1)
+
+ # dN(u; uhat, v*)
+ dN = expand_derivatives(derivative(N, u))
+ vstar, uhat = dN.arguments()
+ assert isinstance(vstar, Coargument)
+
+ # Replace v* by a Form
+ v = TestFunction(V1)
+ F = inner(u, v) * dx
+ G = replace(dN, {vstar: F})
+
+ dN_replaced = dN._ufl_expr_reconstruct_(u, argument_slots=(F, uhat))
+ assert G == dN_replaced
+
+ # Replace v* by an Action
+ M = Matrix(V1, V1)
+ A = Action(M, u)
+ G = replace(dN, {vstar: A})
+
+ dN_replaced = dN._ufl_expr_reconstruct_(u, argument_slots=(A, uhat))
+ assert G == dN_replaced
diff --git a/test/test_extract_blocks.py b/test/test_extract_blocks.py
new file mode 100644
index 000000000..0ce712418
--- /dev/null
+++ b/test/test_extract_blocks.py
@@ -0,0 +1,103 @@
+import pytest
+
+import ufl
+import ufl.algorithms
+from ufl.finiteelement import FiniteElement, MixedElement
+
+
+def epsilon(u):
+ return ufl.sym(ufl.grad(u))
+
+
+def sigma(u, p):
+ return epsilon(u) - p * ufl.Identity(u.ufl_shape[0])
+
+
+@pytest.mark.parametrize("rank", [0, 1, 2])
+def test_extract_blocks(rank):
+ """Test extractions of blocks from mixed function space."""
+ cell = ufl.triangle
+ domain = ufl.Mesh(FiniteElement("Lagrange", cell, 1, (2,), ufl.identity_pullback, ufl.H1))
+ fe_scalar = FiniteElement("Lagrange", cell, 1, (), ufl.identity_pullback, ufl.H1)
+ fe_vector = FiniteElement("Lagrange", cell, 1, (2,), ufl.identity_pullback, ufl.H1)
+
+ me = MixedElement([fe_vector, fe_scalar])
+
+ # # Function spaces
+ W = ufl.FunctionSpace(domain, me)
+ V = ufl.FunctionSpace(domain, fe_vector)
+ Q = ufl.FunctionSpace(domain, fe_scalar)
+
+ if rank == 0:
+ wh = ufl.Coefficient(W)
+ uh, ph = ufl.split(wh)
+ # Test that functionals return the identity
+ J = ufl.inner(sigma(uh, ph), sigma(uh, ph)) * ufl.dx
+ J0 = ufl.extract_blocks(J, 0)
+ assert len(J0) == 1
+ assert J == J0[0]
+ elif rank == 1:
+
+ def rhs(uh, ph, v, q):
+ F_0 = ufl.inner(sigma(uh, ph), epsilon(v)) * ufl.dx(domain=domain)
+ F_1 = ufl.div(uh) * q * ufl.dx
+ return F_0, F_1
+
+ wh = ufl.Coefficient(W)
+ uh, ph = ufl.split(wh)
+ v, q = ufl.TestFunctions(W)
+ F = sum(rhs(uh, ph, v, q))
+
+ v_ = ufl.TestFunction(V)
+ q_ = ufl.TestFunction(Q)
+ F_sub = rhs(uh, ph, ufl.as_vector([vi for vi in v_]), q_)
+
+ F_0_ext = ufl.extract_blocks(F, 0)
+ assert F_sub[0].signature() == F_0_ext.signature()
+
+ F_1_ext = ufl.extract_blocks(F, 1)
+ assert F_sub[1].signature() == F_1_ext.signature()
+ elif rank == 2:
+
+ def lhs(u, p, v, q):
+ J_00 = ufl.inner(u, v) * ufl.dx(domain=domain)
+ J_01 = ufl.div(v) * p * ufl.dx
+ J_10 = q * ufl.div(u) * ufl.dx
+ J_11 = ufl.inner(ufl.grad(p), ufl.grad(q)) * ufl.dx
+ return J_00, J_01, J_10, J_11
+
+ v_ = ufl.TestFunction(V)
+ q_ = ufl.TestFunction(Q)
+ u_ = ufl.TrialFunction(V)
+ p_ = ufl.TrialFunction(Q)
+ J_sub = lhs(ufl.as_vector([ui for ui in u_]), p_, ufl.as_vector([vi for vi in v_]), q_)
+
+ v, q = ufl.TestFunctions(W)
+ uh, ph = ufl.TrialFunctions(W)
+ J = sum(lhs(uh, ph, v, q))
+
+ for i in range(2):
+ for j in range(2):
+ J_ij_ext = ufl.extract_blocks(J, i, j)
+ assert J_sub[2 * i + j].signature() == J_ij_ext.signature()
+
+
+def test_postive_restricted_extract_none():
+ cell = ufl.triangle
+ d = cell.topological_dimension()
+ domain = ufl.Mesh(FiniteElement("Lagrange", cell, 1, (d,), ufl.identity_pullback, ufl.H1))
+ el_u = FiniteElement("Lagrange", cell, 2, (d,), ufl.identity_pullback, ufl.H1)
+ el_p = FiniteElement("Lagrange", cell, 1, (), ufl.identity_pullback, ufl.H1)
+ V = ufl.FunctionSpace(domain, el_u)
+ Q = ufl.FunctionSpace(domain, el_p)
+ W = ufl.MixedFunctionSpace(V, Q)
+ u, p = ufl.TrialFunctions(W)
+ v, q = ufl.TestFunctions(W)
+ a = (
+ ufl.inner(ufl.grad(u), ufl.grad(v)) * ufl.dx
+ + ufl.div(u) * q * ufl.dx
+ + ufl.div(v) * p * ufl.dx
+ )
+ a += ufl.inner(u("+"), v("+")) * ufl.dS
+ a_blocks = ufl.extract_blocks(a)
+ assert a_blocks[1][1] is None
diff --git a/test/test_ffcforms.py b/test/test_ffcforms.py
index 5ab8778ab..9b913947b 100755
--- a/test/test_ffcforms.py
+++ b/test/test_ffcforms.py
@@ -13,9 +13,36 @@
# 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, 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 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
@@ -23,7 +50,7 @@
def testConstant():
element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
- domain = Mesh(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)
@@ -32,15 +59,15 @@ def testConstant():
c = Constant(domain)
d = VectorConstant(domain)
- a = c * dot(grad(v), grad(u)) * dx # noqa: F841
+ _ = c * dot(grad(v), grad(u)) * dx
# FFC notation: L = dot(d, grad(v))*dx
- L = inner(d, grad(v)) * dx # noqa: F841
+ _ = inner(d, grad(v)) * dx
def testElasticity():
- element = FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1))
+ 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)
@@ -51,21 +78,21 @@ def eps(v):
return grad(v) + (grad(v)).T
# FFC notation: a = 0.25*dot(eps(v), eps(u))*dx
- a = 0.25 * inner(eps(v), eps(u)) * dx # noqa: F841
+ _ = 0.25 * inner(eps(v), eps(u)) * dx
def testEnergyNorm():
element = FiniteElement("Lagrange", tetrahedron, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3,), identity_pullback, H1))
space = FunctionSpace(domain, element)
v = Coefficient(space)
- a = (v * v + dot(grad(v), grad(v))) * dx # noqa: F841
+ _ = (v * v + dot(grad(v), grad(v))) * dx
def testEquation():
element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
space = FunctionSpace(domain, element)
k = 0.1
@@ -76,13 +103,13 @@ def testEquation():
F = v * (u - u0) * dx + k * dot(grad(v), grad(0.5 * (u0 + u))) * dx
- a = lhs(F) # noqa: F841
- L = rhs(F) # noqa: F841
+ _ = lhs(F)
+ _ = rhs(F)
def testFunctionOperators():
element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
- domain = Mesh(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)
@@ -92,12 +119,12 @@ def testFunctionOperators():
# FFC notation: a = sqrt(1/modulus(1/f))*sqrt(g)*dot(grad(v), grad(u))*dx
# + v*u*sqrt(f*g)*g*dx
- a = sqrt(1 / abs(1 / f)) * sqrt(g) * dot(grad(v), grad(u)) * dx + v * u * sqrt(f * g) * g * dx # noqa: F841
+ _ = sqrt(1 / abs(1 / f)) * sqrt(g) * dot(grad(v), grad(u)) * dx + v * u * sqrt(f * g) * g * dx
def testHeat():
element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
- domain = Mesh(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)
@@ -107,19 +134,17 @@ def testHeat():
f = Coefficient(space)
k = Constant(domain)
- a = v * u1 * dx + k * c * dot(grad(v), grad(u1)) * dx # noqa: F841
- L = v * u0 * dx + k * v * f * dx # noqa: F841
+ _ = v * u1 * dx + k * c * dot(grad(v), grad(u1)) * dx
+ _ = v * u0 * dx + k * v * f * dx
def testMass():
element = FiniteElement("Lagrange", tetrahedron, 3, (), identity_pullback, H1)
- domain = Mesh(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)
u = TrialFunction(space)
-
- a = v * u * dx # noqa: F841
+ _ = v * u * dx
def testMixedMixedElement():
@@ -129,40 +154,36 @@ def testMixedMixedElement():
def testMixedPoisson():
q = 1
-
- BDM = FiniteElement("Brezzi-Douglas-Marini", triangle, q, (2, ), contravariant_piola, HDiv)
+ BDM = FiniteElement("Brezzi-Douglas-Marini", triangle, q, (2,), contravariant_piola, HDiv)
DG = FiniteElement("Discontinuous Lagrange", triangle, q - 1, (), identity_pullback, L2)
mixed_element = MixedElement([BDM, DG])
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
space = FunctionSpace(domain, mixed_element)
(tau, w) = TestFunctions(space)
(sigma, u) = TrialFunctions(space)
-
f = Coefficient(FunctionSpace(domain, DG))
-
- a = (dot(tau, sigma) - div(tau) * u + w * div(sigma)) * dx # noqa: F841
- L = w * f * dx # noqa: F841
+ _ = (dot(tau, sigma) - div(tau) * u + w * div(sigma)) * dx
+ _ = w * f * dx
def testNavierStokes():
- element = FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1))
+ 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)
u = TrialFunction(space)
-
w = Coefficient(space)
# FFC notation: a = v[i]*w[j]*D(u[i], j)*dx
- a = v[i] * w[j] * Dx(u[i], j) * dx # noqa: F841
+ _ = v[i] * w[j] * Dx(u[i], j) * dx
def testNeumannProblem():
- element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)
@@ -171,23 +192,21 @@ def testNeumannProblem():
g = Coefficient(space)
# FFC notation: a = dot(grad(v), grad(u))*dx
- a = inner(grad(v), grad(u)) * dx # noqa: F841
+ _ = inner(grad(v), grad(u)) * dx
# FFC notation: L = dot(v, f)*dx + dot(v, g)*ds
- L = inner(v, f) * dx + inner(v, g) * ds # noqa: F841
+ _ = inner(v, f) * dx + inner(v, g) * ds
def testOptimization():
element = FiniteElement("Lagrange", triangle, 3, (), identity_pullback, H1)
- domain = Mesh(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)
f = Coefficient(space)
-
- a = dot(grad(v), grad(u)) * dx # noqa: F841
- L = v * f * dx # noqa: F841
+ _ = dot(grad(v), grad(u)) * dx
+ _ = v * f * dx
def testP5tet():
@@ -200,20 +219,18 @@ def testP5tri():
def testPoissonDG():
element = FiniteElement("Discontinuous Lagrange", triangle, 1, (), identity_pullback, L2)
- domain = Mesh(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)
f = Coefficient(space)
-
n = FacetNormal(domain)
# FFC notation: h = MeshSize(domain), not supported by UFL
h = Constant(domain)
gN = Coefficient(space)
-
alpha = 4.0
gamma = 8.0
@@ -229,17 +246,17 @@ def testPoissonDG():
a = inner(grad(v), grad(u)) * dx
a -= inner(avg(grad(v)), jump(u, n)) * dS
a -= inner(jump(v, n), avg(grad(u))) * dS
- a += alpha / h('+') * dot(jump(v, n), jump(u, n)) * dS
+ a += alpha / h("+") * dot(jump(v, n), jump(u, n)) * dS
a -= inner(grad(v), u * n) * ds
a -= inner(u * n, grad(u)) * ds
a += gamma / h * v * u * ds
- L = v * f * dx + v * gN * ds # noqa: F841
+ _ = v * f * dx + v * gN * ds
def testPoisson():
element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
- domain = Mesh(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)
@@ -247,13 +264,13 @@ def testPoisson():
f = Coefficient(space)
# Note: inner() also works
- a = dot(grad(v), grad(u)) * dx # noqa: F841
- L = v * f * dx # noqa: F841
+ _ = dot(grad(v), grad(u)) * dx
+ _ = v * f * dx
def testPoissonSystem():
- element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)
@@ -261,10 +278,10 @@ def testPoissonSystem():
f = Coefficient(space)
# FFC notation: a = dot(grad(v), grad(u))*dx
- a = inner(grad(v), grad(u)) * dx # noqa: F841
+ _ = inner(grad(v), grad(u)) * dx
# FFC notation: L = dot(v, f)*dx
- L = inner(v, f) * dx # noqa: F841
+ _ = inner(v, f) * dx
def testProjection():
@@ -273,11 +290,10 @@ def testProjection():
# projection can be extended to handle also local projections.
P1 = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
space = FunctionSpace(domain, P1)
-
- v = TestFunction(space) # noqa: F841
- f = Coefficient(space) # noqa: F841
+ _ = TestFunction(space)
+ _ = Coefficient(space)
# pi0 = Projection(P0)
# pi1 = Projection(P1)
@@ -294,9 +310,9 @@ def testQuadratureElement():
# sig = VectorQuadratureElement(triangle, 3)
QE = FiniteElement("Quadrature", triangle, 3, (), identity_pullback, L2)
- sig = FiniteElement("Quadrature", triangle, 3, (2, ), identity_pullback, L2)
+ sig = FiniteElement("Quadrature", triangle, 3, (2,), identity_pullback, L2)
- domain = Mesh(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)
@@ -306,53 +322,48 @@ def testQuadratureElement():
sig0 = Coefficient(FunctionSpace(domain, sig))
f = Coefficient(space)
- a = v.dx(i) * C * u.dx(i) * dx + v.dx(i) * 2 * u0 * u * u0.dx(i) * dx # noqa: F841
- L = v * f * dx - dot(grad(v), sig0) * dx # noqa: F841
+ _ = v.dx(i) * C * u.dx(i) * dx + v.dx(i) * 2 * u0 * u * u0.dx(i) * dx
+ _ = v * f * dx - dot(grad(v), sig0) * dx
def testStokes():
# UFLException: Shape mismatch in sum.
- P2 = FiniteElement("Lagrange", triangle, 2, (2, ), identity_pullback, H1)
+ P2 = FiniteElement("Lagrange", triangle, 2, (2,), identity_pullback, H1)
P1 = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
TH = MixedElement([P2, P1])
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
th_space = FunctionSpace(domain, TH)
p2_space = FunctionSpace(domain, P2)
(v, q) = TestFunctions(th_space)
(u, p) = TrialFunctions(th_space)
-
f = Coefficient(p2_space)
# FFC notation:
# a = (dot(grad(v), grad(u)) - div(v)*p + q*div(u))*dx
- a = (inner(grad(v), grad(u)) - div(v) * p + q * div(u)) * dx # noqa: F841
-
- L = dot(v, f) * dx # noqa: F841
+ _ = (inner(grad(v), grad(u)) - div(v) * p + q * div(u)) * dx
+ _ = dot(v, f) * dx
def testSubDomain():
element = FiniteElement("Lagrange", tetrahedron, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3,), identity_pullback, H1))
space = FunctionSpace(domain, element)
-
f = Coefficient(space)
-
- M = f * dx(2) + f * ds(5) # noqa: F841
+ _ = f * dx(2) + f * ds(5)
def testSubDomains():
element = FiniteElement("Lagrange", tetrahedron, 1, (), identity_pullback, H1)
- domain = Mesh(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)
u = TrialFunction(space)
-
a = v * u * dx(0) + 10.0 * v * u * dx(1) + v * u * ds(0) + 2.0 * v * u * ds(1)
- a += v('+') * u('+') * dS(0) + 4.3 * v('+') * u('+') * dS(1)
+ a += v("+") * u("+") * dS(0) + 4.3 * v("+") * u("+") * dS(1)
def testTensorWeightedPoisson():
@@ -376,15 +387,14 @@ def testTensorWeightedPoisson():
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))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
p1_space = FunctionSpace(domain, P1)
p0_space = FunctionSpace(domain, P0)
v = TestFunction(p1_space)
u = TrialFunction(p1_space)
C = Coefficient(p0_space)
-
- a = inner(grad(v), C * grad(u)) * dx # noqa: F841
+ _ = inner(grad(v), C * grad(u)) * dx
def testVectorLaplaceGradCurl():
@@ -395,8 +405,12 @@ def HodgeLaplaceGradCurl(space, fspace):
# FFC notation: a = (dot(tau, sigma) - dot(grad(tau), u) + dot(v,
# grad(sigma)) + dot(curl(v), curl(u)))*dx
- a = (inner(tau, sigma) - inner(grad(tau), u) +
- inner(v, grad(sigma)) + inner(curl(v), curl(u))) * dx
+ a = (
+ inner(tau, sigma)
+ - inner(grad(tau), u)
+ + inner(v, grad(sigma))
+ + inner(curl(v), curl(u))
+ ) * dx
# FFC notation: L = dot(v, f)*dx
L = inner(v, f) * dx
@@ -409,10 +423,11 @@ def HodgeLaplaceGradCurl(space, fspace):
GRAD = FiniteElement("Lagrange", shape, order, (), identity_pullback, H1)
# FFC notation: CURL = FiniteElement("Nedelec", shape, order-1)
- CURL = FiniteElement("N1curl", shape, order, (3, ), covariant_piola, HCurl)
+ CURL = FiniteElement("N1curl", shape, order, (3,), covariant_piola, HCurl)
- VectorLagrange = FiniteElement("Lagrange", shape, order + 1, (3, ), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", shape, 1, (3, ), identity_pullback, H1))
+ 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, MixedElement([GRAD, CURL])),
- FunctionSpace(domain, VectorLagrange))
+ [a, L] = HodgeLaplaceGradCurl(
+ FunctionSpace(domain, MixedElement([GRAD, CURL])), FunctionSpace(domain, VectorLagrange)
+ )
diff --git a/test/test_form.py b/test/test_form.py
index 30ca5117a..e83abcfcf 100755
--- a/test/test_form.py
+++ b/test/test_form.py
@@ -1,7 +1,23 @@
import pytest
-from ufl import (Coefficient, Cofunction, Form, FormSum, FunctionSpace, Mesh, SpatialCoordinate, TestFunction,
- TrialFunction, 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
@@ -18,7 +34,7 @@ def element():
@pytest.fixture
def domain():
cell = triangle
- return Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1))
+ return Mesh(FiniteElement("Lagrange", cell, 1, (2,), identity_pullback, H1))
@pytest.fixture
@@ -44,7 +60,7 @@ def stiffness(domain):
@pytest.fixture
def convection(domain):
cell = triangle
- element = FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)
+ element = FiniteElement("Lagrange", cell, 1, (2,), identity_pullback, H1)
space = FunctionSpace(domain, element)
v = TestFunction(space)
u = TrialFunction(space)
@@ -74,7 +90,7 @@ def boundary_load(domain):
def test_form_arguments(mass, stiffness, convection, load):
v, u = mass.arguments()
- f, = load.coefficients()
+ (f,) = load.coefficients()
assert v.number() == 0
assert u.number() == 1
@@ -104,7 +120,7 @@ def test_form_coefficients(element, domain):
def test_form_domains():
cell = triangle
element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", cell, 1, (2,), identity_pullback, H1))
V = FunctionSpace(domain, element)
v = TestFunction(V)
@@ -136,32 +152,33 @@ def test_form_integrals(mass, boundary_load):
def test_form_call():
element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
V = FunctionSpace(domain, element)
v = TestFunction(V)
u = TrialFunction(V)
f = Coefficient(V)
g = Coefficient(V)
- a = g*inner(grad(v), grad(u))*dx
+ a = g * inner(grad(v), grad(u)) * dx
M = a(f, f, coefficients={g: 1})
- assert M == grad(f)**2*dx
+ assert M == grad(f) ** 2 * dx
import sys
+
if sys.version_info.major >= 3 and sys.version_info.minor >= 5:
- a = u*v*dx
+ a = u * v * dx
M = eval("(a @ f) @ g")
- assert M == g*f*dx
+ assert M == g * f * dx
def test_formsum(mass):
element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
V = FunctionSpace(domain, element)
v = Cofunction(V.dual())
assert v + mass
assert mass + v
- assert isinstance((mass+v), FormSum)
+ assert isinstance((mass + v), FormSum)
assert len((mass + v + v).components()) == 3
# Variational forms are summed appropriately
@@ -169,7 +186,7 @@ def test_formsum(mass):
assert v - mass
assert mass - v
- assert isinstance((mass+v), FormSum)
+ assert isinstance((mass + v), FormSum)
assert -v
assert isinstance(-v, BaseForm)
diff --git a/test/test_grad.py b/test/test_grad.py
deleted file mode 100755
index 21b269e2f..000000000
--- a/test/test_grad.py
+++ /dev/null
@@ -1,133 +0,0 @@
-"""Test use of grad in various situations."""
-
-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):
- _test_grad_div_curl_properties(self, interval)
-
-
-def xtest_grad_div_curl_properties_in_2D(self):
- _test_grad_div_curl_properties(self, triangle)
-
-
-def xtest_grad_div_curl_properties_in_3D(self):
- _test_grad_div_curl_properties(self, tetrahedron)
-
-
-def _test_grad_div_curl_properties(self, cell):
- d = cell.geometric_dimension()
-
- 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)
- ct = TensorConstant(cell)
-
- s = Coefficient(S)
- v = Coefficient(V)
- t = Coefficient(T)
-
- def eval_s(x, derivatives=()):
- return sum(derivatives)
-
- def eval_v(x, derivatives=()):
- return tuple(float(k)+sum(derivatives) for k in range(d))
-
- def eval_t(x, derivatives=()):
- return tuple(tuple(float(i*j)+sum(derivatives)
- for i in range(d))
- for j in range(d))
-
- mapping = {cs: eval_s, s: eval_s,
- cv: eval_v, v: eval_v,
- ct: eval_t, t: eval_t, }
- x = tuple(1.0+float(k) for k in range(d))
-
- assert s.ufl_shape == ()
- assert v.ufl_shape == (d,)
- assert t.ufl_shape == (d, d)
-
- assert cs.ufl_shape == ()
- assert cv.ufl_shape == (d,)
- assert ct.ufl_shape == (d, d)
-
- self.assertEqual(s(x, mapping=mapping), eval_s(x))
- self.assertEqual(v(x, mapping=mapping), eval_v(x))
- self.assertEqual(t(x, mapping=mapping), eval_t(x))
-
- assert grad(s).ufl_shape == (d,)
- assert grad(v).ufl_shape == (d, d)
- assert grad(t).ufl_shape == (d, d, d)
-
- assert grad(cs).ufl_shape == (d,)
- assert grad(cv).ufl_shape == (d, d)
- assert grad(ct).ufl_shape == (d, d, d)
-
- self.assertEqual(grad(s)[0](x, mapping=mapping), eval_s(x, (0,)))
- self.assertEqual(grad(v)[d-1, d-1](x, mapping=mapping),
- eval_v(x, derivatives=(d-1,))[d-1])
- self.assertEqual(grad(t)[d-1, d-1, d-1](x, mapping=mapping),
- eval_t(x, derivatives=(d-1,))[d-1][d-1])
-
- assert div(grad(cs)).ufl_shape == ()
- assert div(grad(cv)).ufl_shape == (d,)
- assert div(grad(ct)).ufl_shape == (d, d)
-
- assert s.dx(0).ufl_shape == ()
- assert v.dx(0).ufl_shape == (d,)
- assert t.dx(0).ufl_shape == (d, d)
-
- assert s.dx(0 == 0).ufl_shape, ()
- assert v.dx(0 == 0).ufl_shape, (d,)
- assert t.dx(0 == 0).ufl_shape, (d, d)
-
- i, j = indices(2)
- assert s.dx(i).ufl_shape == ()
- assert v.dx(i).ufl_shape == (d,)
- assert t.dx(i).ufl_shape == (d, d)
-
- assert s.dx(i).ufl_free_indices == (i.count(),)
- assert v.dx(i).ufl_free_indices == (i.count(),)
- assert t.dx(i).ufl_free_indices == (i.count(),)
-
- self.assertEqual(s.dx(i, j).ufl_shape, ())
- self.assertEqual(v.dx(i, j).ufl_shape, (d,))
- self.assertEqual(t.dx(i, j).ufl_shape, (d, d))
-
- self.assertTrue(s.dx(i, j).ufl_free_indices == tuple(sorted([i.count(), j.count()])))
- self.assertTrue(v.dx(i, j).ufl_free_indices == tuple(sorted([i.count(), j.count()])))
- self.assertTrue(t.dx(i, j).ufl_free_indices == tuple(sorted([i.count(), j.count()])))
-
- a0 = s.dx(0)*dx
- a1 = s.dx(0)**2*dx
- a2 = v.dx(0)**2*dx
- a3 = t.dx(0)**2*dx
-
- a4 = inner(grad(s), grad(s))*dx
- a5 = inner(grad(v), grad(v))*dx
- a6 = inner(grad(t), grad(t))*dx
-
- a7 = inner(div(grad(s)), s)*dx
- a8 = inner(div(grad(v)), v)*dx
- a9 = inner(div(grad(t)), t)*dx
-
- compute_form_data(a0)
- compute_form_data(a1)
- compute_form_data(a2)
- compute_form_data(a3)
-
- compute_form_data(a4)
- compute_form_data(a5)
- compute_form_data(a6)
-
- compute_form_data(a7)
- compute_form_data(a8)
- compute_form_data(a9)
diff --git a/test/test_illegal.py b/test/test_illegal.py
index 9931946a4..acc2d33d5 100755
--- a/test/test_illegal.py
+++ b/test/test_illegal.py
@@ -15,12 +15,12 @@ def selement():
@pytest.fixture
def velement():
- return FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
+ return FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1)
@pytest.fixture
def domain():
- return Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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 4ffdc7eb5..8b4d69d29 100755
--- a/test/test_indexing.py
+++ b/test/test_indexing.py
@@ -9,7 +9,7 @@
@pytest.fixture
def domain():
- return Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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 49cf16a1a..417648095 100755
--- a/test/test_indices.py
+++ b/test/test_indices.py
@@ -1,85 +1,109 @@
import pytest
-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)
+import ufl.algorithms
+import ufl.classes
+from ufl import (
+ Argument,
+ Coefficient,
+ FunctionSpace,
+ Mesh,
+ TestFunction,
+ TrialFunction,
+ as_matrix,
+ as_tensor,
+ as_vector,
+ cos,
+ dx,
+ exp,
+ i,
+ indices,
+ interval,
+ 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...
+# TODO: add more expressions to test as many possible combinations of
+# index notation as feasible...
def test_vector_indices(self):
- element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)
- u[i]*f[i]*dx
- u[j]*f[j]*dx
+ u[i] * f[i] * dx
+ u[j] * f[j] * dx
def test_tensor_indices(self):
element = FiniteElement("Lagrange", triangle, 1, (2, 2), identity_pullback, H1)
- domain = Mesh(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)
- u[i, j]*f[i, j]*dx
- u[j, i]*f[i, j]*dx
- u[j, i]*f[j, i]*dx
+ u[i, j] * f[i, j] * dx
+ u[j, i] * f[i, j] * dx
+ u[j, i] * f[j, i] * dx
with pytest.raises(BaseException):
- (u[i, i]+f[j, i])*dx
+ (u[i, i] + f[j, i]) * dx
def test_indexed_sum1(self):
- element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)
- a = u[i]+f[i]
+ a = u[i] + f[i]
with pytest.raises(BaseException):
- a*dx
+ a * dx
def test_indexed_sum2(self):
- element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)
f = Coefficient(space)
- a = u[j]+f[j]+v[j]+2*v[j]+exp(u[i]*u[i])/2*f[j]
+ a = u[j] + f[j] + v[j] + 2 * v[j] + exp(u[i] * u[i]) / 2 * f[j]
with pytest.raises(BaseException):
- a*dx
+ a * dx
def test_indexed_sum3(self):
- element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)
with pytest.raises(BaseException):
- u[i]+f[j]
+ u[i] + f[j]
def test_indexed_function1(self):
- element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)
f = Coefficient(space)
- aarg = (u[i]+f[i])*v[i]
- exp(aarg)*dx
+ aarg = (u[i] + f[i]) * v[i]
+ exp(aarg) * dx
def test_indexed_function2(self):
- element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)
@@ -95,19 +119,19 @@ def test_indexed_function2(self):
def test_indexed_function3(self):
- element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)
f = Coefficient(space)
with pytest.raises(BaseException):
- sin(u[i] + f[i])*dx
+ sin(u[i] + f[i]) * dx
def test_vector_from_indices(self):
- element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)
@@ -124,14 +148,14 @@ def test_vector_from_indices(self):
def test_matrix_from_indices(self):
- element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)
- A = as_matrix(u[i]*v[j], (i, j))
- B = as_matrix(v[k]*v[k]*u[i]*v[j], (j, i))
+ A = as_matrix(u[i] * v[j], (i, j))
+ B = as_matrix(v[k] * v[k] * u[i] * v[j], (j, i))
C = A + A
C = B + B
D = A + B
@@ -142,8 +166,8 @@ def test_matrix_from_indices(self):
def test_vector_from_list(self):
- element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)
@@ -156,8 +180,8 @@ def test_vector_from_list(self):
def test_matrix_from_list(self):
- element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)
@@ -165,7 +189,7 @@ def test_matrix_from_list(self):
# create matrix from list
A = as_matrix([[u[0], u[1]], [v[0], v[1]]])
# create matrix from indices
- B = as_matrix((v[k]*v[k]) * u[i]*v[j], (j, i))
+ B = as_matrix((v[k] * v[k]) * u[i] * v[j], (j, i))
# Test addition
C = A + A
C = B + B
@@ -177,8 +201,8 @@ def test_matrix_from_list(self):
def test_tensor(self):
- element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)
@@ -186,7 +210,7 @@ def test_tensor(self):
g = Coefficient(space)
# define the components of a fourth order tensor
- Cijkl = u[i]*v[j]*f[k]*g[l]
+ Cijkl = u[i] * v[j] * f[k] * g[l]
assert len(Cijkl.ufl_shape) == 0
assert set(Cijkl.ufl_free_indices) == {i.count(), j.count(), k.count(), l.count()}
@@ -205,7 +229,7 @@ def test_tensor(self):
# legal?
vv = as_vector([u[i], v[i]])
- f[i]*vv # this is well defined: ww = sum_i
+ f[i] * vv # this is well defined: ww = sum_i
# illegal
with pytest.raises(BaseException):
@@ -217,8 +241,8 @@ def test_tensor(self):
def test_indexed(self):
- element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)
@@ -238,13 +262,13 @@ def test_indexed(self):
def test_spatial_derivative(self):
cell = triangle
- element = FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1))
+ 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)
i, j, k, l = indices(4) # noqa: E741
- d = cell.geometric_dimension()
+ d = 2
a = v[i].dx(i)
self.assertSameIndices(a, ())
@@ -256,7 +280,7 @@ def test_spatial_derivative(self):
self.assertNotIsInstance(a, IndexSum)
assert a.ufl_shape == ()
- a = (v[i]*u[j]).dx(i, j)
+ a = (v[i] * u[j]).dx(i, j)
self.assertSameIndices(a, ())
self.assertIsInstance(a, IndexSum)
assert a.ufl_shape == ()
@@ -272,7 +296,7 @@ def test_spatial_derivative(self):
self.assertNotIsInstance(a, IndexSum)
assert a.ufl_shape == ()
- a = (v[i]*u[j]).dx(0, 1)
+ a = (v[i] * u[j]).dx(0, 1)
assert set(a.ufl_free_indices) == {i.count(), j.count()}
self.assertNotIsInstance(a, IndexSum)
assert a.ufl_shape == ()
@@ -284,4 +308,33 @@ def test_spatial_derivative(self):
def test_renumbering(self):
- pass
+ """Test that kernels with common integral data, but different index numbering,
+ are correctly renumbered."""
+ cell = interval
+ mesh = Mesh(FiniteElement("Lagrange", cell, 1, (2,), identity_pullback, H1))
+ V = FunctionSpace(mesh, FiniteElement("Lagrange", cell, 1, (2,), identity_pullback, H1))
+ v = TestFunction(V)
+ u = TrialFunction(V)
+ i = indices(1)
+ a0 = u[i].dx(0) * v[i].dx(0) * ufl.dx((1))
+ a1 = (
+ u[i].dx(0)
+ * v[i].dx(0)
+ * ufl.dx(
+ (
+ 2,
+ 3,
+ )
+ )
+ )
+ form_data = ufl.algorithms.compute_form_data(
+ a0 + a1,
+ do_apply_function_pullbacks=True,
+ do_apply_integral_scaling=True,
+ do_apply_geometry_lowering=True,
+ preserve_geometry_types=(ufl.classes.Jacobian,),
+ do_apply_restrictions=True,
+ do_append_everywhere_integrals=False,
+ )
+
+ assert len(form_data.integral_data) == 1
diff --git a/test/test_interpolate.py b/test/test_interpolate.py
index 71f3cd145..877f55a35 100644
--- a/test/test_interpolate.py
+++ b/test/test_interpolate.py
@@ -5,11 +5,31 @@
import pytest
-from ufl import (Action, Adjoint, Argument, Coefficient, FunctionSpace, Mesh, TestFunction, TrialFunction, 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.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
@@ -19,7 +39,7 @@
@pytest.fixture
def domain_2d():
- return Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ return Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
@pytest.fixture
@@ -35,7 +55,6 @@ def V2(domain_2d):
def test_symbolic(V1, V2):
-
# Set dual of V2
V2_dual = V2.dual()
@@ -51,7 +70,6 @@ def test_symbolic(V1, V2):
def test_action_adjoint(V1, V2):
-
# Set dual of V2
V2_dual = V2.dual()
vstar = Argument(V2_dual, 0)
@@ -78,7 +96,6 @@ def test_action_adjoint(V1, V2):
def test_differentiation(V1, V2):
-
u = Coefficient(V1)
v = TestFunction(V1)
@@ -133,7 +150,6 @@ def test_differentiation(V1, V2):
def test_extract_base_form_operators(V1, V2):
-
u = Coefficient(V1)
uhat = TrialFunction(V1)
vstar = Argument(V2.dual(), 0)
diff --git a/test/test_lhs_rhs.py b/test/test_lhs_rhs.py
index 2be0e584c..bb99eab3a 100755
--- a/test/test_lhs_rhs.py
+++ b/test/test_lhs_rhs.py
@@ -3,8 +3,23 @@
# First added: 2011-11-09
# Last changed: 2011-11-09
-from ufl import (Argument, Coefficient, Constant, FunctionSpace, Mesh, TestFunction, TrialFunction, 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
@@ -12,7 +27,7 @@
def test_lhs_rhs_simple():
V = FiniteElement("Lagrange", interval, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", interval, 1, (1, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", interval, 1, (1,), identity_pullback, H1))
space = FunctionSpace(domain, V)
v = TestFunction(space)
u = TrialFunction(space)
@@ -41,13 +56,13 @@ def test_lhs_rhs_simple():
def test_lhs_rhs_derivatives():
V = FiniteElement("Lagrange", interval, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", interval, 1, (1, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", interval, 1, (1,), identity_pullback, H1))
space = FunctionSpace(domain, V)
v = TestFunction(space)
u = TrialFunction(space)
f = Coefficient(space)
- F0 = exp(f) * u * v * dx + v * dx + f * v * ds + exp(f)('+') * v * dS
+ F0 = exp(f) * u * v * dx + v * dx + f * v * ds + exp(f)("+") * v * dS
a, L = system(F0)
assert len(a.integrals()) == 1
assert len(L.integrals()) == 3
@@ -58,7 +73,7 @@ def test_lhs_rhs_derivatives():
def test_lhs_rhs_slightly_obscure():
V = FiniteElement("Lagrange", interval, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", interval, 1, (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_literals.py b/test/test_literals.py
index aed180b24..41ca4460b 100755
--- a/test/test_literals.py
+++ b/test/test_literals.py
@@ -1,6 +1,8 @@
__authors__ = "Martin Sandve Alnæs"
__date__ = "2011-04-14"
+import numpy as np
+
from ufl import PermutationSymbol, as_matrix, as_vector, indices, product
from ufl.classes import Indexed
from ufl.constantvalue import ComplexValue, FloatValue, IntValue, Zero, as_ufl
@@ -16,7 +18,8 @@ def test_zero(self):
self.assertNotEqual(z1, 1.0)
self.assertFalse(z1)
- # If zero() == 0 is to be allowed, it must not have the same hash or it will collide with 0 as key in dicts...
+ # If zero() == 0 is to be allowed, it must not have the same hash or
+ # it will collide with 0 as key in dicts...
self.assertNotEqual(hash(z1), hash(0.0))
self.assertNotEqual(hash(z1), hash(0))
@@ -28,6 +31,7 @@ def test_float(self):
f4 = FloatValue(1.0)
f5 = 3 - FloatValue(1) - 1
f6 = 3 * FloatValue(2) / 6
+ f7 = as_ufl(np.ones((1,), dtype="d")[0])
assert f1 == f1
self.assertNotEqual(f1, f2) # IntValue vs FloatValue, == compares representations!
@@ -35,6 +39,7 @@ def test_float(self):
assert f2 == f4
assert f2 == f5
assert f2 == f6
+ assert f2 == f7
def test_int(self):
@@ -44,6 +49,7 @@ def test_int(self):
f4 = IntValue(1.0)
f5 = 3 - IntValue(1) - 1
f6 = 3 * IntValue(2) / 6
+ f7 = as_ufl(np.ones((1,), dtype="int")[0])
assert f1 == f1
self.assertNotEqual(f1, f2) # IntValue vs FloatValue, == compares representations!
@@ -51,6 +57,7 @@ def test_int(self):
assert f1 == f4
assert f1 == f5
assert f2 == f6 # Division produces a FloatValue
+ assert f1 == f7
def test_complex(self):
@@ -61,6 +68,7 @@ def test_complex(self):
f5 = ComplexValue(1.0 + 1.0j)
f6 = as_ufl(1.0)
f7 = as_ufl(1.0j)
+ f8 = as_ufl(np.array([1 + 1j], dtype="complex")[0])
assert f1 == f1
assert f1 == f4
@@ -70,6 +78,7 @@ def test_complex(self):
assert f5 == f2 + f3
assert f4 == f5
assert f6 + f7 == f2 + f3
+ assert f4 == f8
def test_scalar_sums(self):
@@ -77,7 +86,7 @@ def test_scalar_sums(self):
s = [as_ufl(i) for i in range(n)]
for i in range(n):
- self.assertNotEqual(s[i], i+1)
+ self.assertNotEqual(s[i], i + 1)
for i in range(n):
assert s[i] == i
@@ -98,9 +107,9 @@ def test_scalar_sums(self):
assert s[1] + s[2] == 3
assert s[1] + s[2] + s[3] == s[6]
assert s[5] - s[2] == 3
- assert 1*s[5] == 5
- assert 2*s[5] == 10
- assert s[6]/3 == 2
+ assert 1 * s[5] == 5
+ assert 2 * s[5] == 10
+ assert s[6] / 3 == 2
def test_identity(self):
@@ -114,7 +123,7 @@ def test_permutation_symbol_3(self):
for i in range(3):
for j in range(3):
for k in range(3):
- value = (j-i)*(k-i)*(k-j)/2
+ value = (j - i) * (k - i) * (k - j) / 2
self.assertEqual(e[i, j, k], value)
i, j, k = indices(3)
self.assertIsInstance(e[i, j, k], Indexed)
@@ -125,17 +134,18 @@ def test_permutation_symbol_3(self):
def test_permutation_symbol_n(self):
for n in range(2, 5): # tested with upper limit 7, but evaluation is a bit slow then
e = PermutationSymbol(n)
- assert e.ufl_shape == (n,)*n
+ assert e.ufl_shape == (n,) * n
assert eval(repr(e)) == e
ii = indices(n)
- x = (0,)*n
- nfac = product(m for m in range(1, n+1))
+ x = (0,) * n
+ nfac = product(m for m in range(1, n + 1))
assert (e[ii] * e[ii])(x) == nfac
def test_unit_dyads(self):
from ufl.tensors import unit_matrices, unit_vectors
+
ei, ej = unit_vectors(2)
self.assertEqual(as_vector((1, 0)), ei)
self.assertEqual(as_vector((0, 1)), ej)
diff --git a/test/test_measures.py b/test/test_measures.py
index 2a31d51e1..eaaffd745 100755
--- a/test/test_measures.py
+++ b/test/test_measures.py
@@ -60,21 +60,20 @@ 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(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)))
+ one * dx(Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1)))
def test_foo():
-
# Define a manifold domain, allows checking gdim/tdim mixup errors
gdim = 3
tdim = 2
- cell = Cell("triangle", gdim)
+ cell = Cell("triangle")
mymesh = MockMesh(9)
- mydomain = Mesh(FiniteElement("Lagrange", cell, 1, (gdim, ), identity_pullback, H1),
- 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
assert cell.cellname() == "triangle"
assert mydomain.topological_dimension() == tdim
assert mydomain.geometric_dimension() == gdim
@@ -88,10 +87,7 @@ def test_foo():
# Test definition of a custom measure with explicit parameters
metadata = {"opt": True}
- mydx = Measure("dx",
- domain=mydomain,
- subdomain_id=3,
- metadata=metadata)
+ mydx = Measure("dx", domain=mydomain, subdomain_id=3, metadata=metadata)
assert mydx.ufl_domain().ufl_id() == mydomain.ufl_id()
assert mydx.metadata() == metadata
M = f * mydx
@@ -121,7 +117,7 @@ def test_foo():
# Set subdomain_id to (2,3), still no domain set
dx23 = dx((2, 3))
assert dx23.ufl_domain() is None
- assert dx23.subdomain_id(), (2 == 3)
+ assert dx23.subdomain_id(), 2 == 3
# Map metadata to metadata, ffc interprets as before
dxm = dx(metadata={"dummy": 123})
@@ -162,29 +158,29 @@ def test_foo():
# Create some forms with these measures (used in checks below):
Mx = f * dxd
- Ms = f ** 2 * dsd
- MS = f('+') * dSd
- M = f * dxd + f ** 2 * dsd + f('+') * dSd
+ Ms = f**2 * dsd
+ MS = f("+") * dSd
+ M = f * dxd + f**2 * dsd + f("+") * dSd
# Test extracting domain data from a form for each measure:
- domain, = Mx.ufl_domains()
+ (domain,) = Mx.ufl_domains()
assert domain.ufl_id() == mydomain.ufl_id()
assert domain.ufl_cargo() == mymesh
assert len(Mx.subdomain_data()[mydomain]["cell"]) == 1
assert Mx.subdomain_data()[mydomain]["cell"][0] == cell_domains
- domain, = Ms.ufl_domains()
+ (domain,) = Ms.ufl_domains()
assert domain.ufl_cargo() == mymesh
assert len(Ms.subdomain_data()[mydomain]["exterior_facet"]) == 1
assert Ms.subdomain_data()[mydomain]["exterior_facet"][0] == exterior_facet_domains
- domain, = MS.ufl_domains()
+ (domain,) = MS.ufl_domains()
assert domain.ufl_cargo() == mymesh
assert len(MS.subdomain_data()[mydomain]["interior_facet"]) == 1
assert MS.subdomain_data()[mydomain]["interior_facet"][0] == interior_facet_domains
# Test joining of these domains in a single form
- domain, = M.ufl_domains()
+ (domain,) = M.ufl_domains()
assert domain.ufl_cargo() == mymesh
assert len(M.subdomain_data()[mydomain]["cell"]) == 1
assert M.subdomain_data()[mydomain]["cell"][0] == cell_domains
diff --git a/test/test_mixed_function_space.py b/test/test_mixed_function_space.py
index 6e3e68356..476be6271 100644
--- a/test/test_mixed_function_space.py
+++ b/test/test_mixed_function_space.py
@@ -1,8 +1,17 @@
__authors__ = "Cecile Daversin Catty"
__date__ = "2019-03-26 -- 2019-03-26"
-from ufl import (FunctionSpace, Measure, Mesh, MixedFunctionSpace, TestFunctions, TrialFunctions, 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
@@ -11,9 +20,9 @@
def test_mixed_functionspace(self):
# Domains
- 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))
+ 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("Lagrange", interval, 1, (), identity_pullback, H1)
f_2d = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
@@ -42,20 +51,20 @@ def test_mixed_functionspace(self):
# Mixed variational form
# LHS
- a_11 = u_1d*v_1d*dx1
- a_22 = u_2d*v_2d*dx2
- a_33 = u_3d*v_3d*dx3
- a_21 = u_2d*v_1d*dx1
- a_12 = u_1d*v_2d*dx1
- a_32 = u_3d*v_2d*dx2
- a_23 = u_2d*v_3d*dx2
- a_31 = u_3d*v_1d*dx1
- a_13 = u_1d*v_3d*dx1
+ a_11 = u_1d * v_1d * dx1
+ a_22 = u_2d * v_2d * dx2
+ a_33 = u_3d * v_3d * dx3
+ a_21 = u_2d * v_1d * dx1
+ a_12 = u_1d * v_2d * dx1
+ a_32 = u_3d * v_2d * dx2
+ a_23 = u_2d * v_3d * dx2
+ a_31 = u_3d * v_1d * dx1
+ a_13 = u_1d * v_3d * dx1
a = a_11 + a_22 + a_33 + a_21 + a_12 + a_32 + a_23 + a_31 + a_13
# RHS
- f_1 = v_1d*dx1
- f_2 = v_2d*dx2
- f_3 = v_3d*dx3
+ f_1 = v_1d * dx1
+ f_2 = v_2d * dx2
+ f_3 = v_3d * dx3
f = f_1 + f_2 + f_3
# Check extract_block algorithm
diff --git a/test/test_new_ad.py b/test/test_new_ad.py
index be6b6d403..38ef9de44 100755
--- a/test/test_new_ad.py
+++ b/test/test_new_ad.py
@@ -1,7 +1,32 @@
-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 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
@@ -17,11 +42,11 @@
def test_apply_derivatives_doesnt_change_expression_without_derivatives():
cell = triangle
- d = cell.geometric_dimension()
+ d = 2
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))
+ domain = Mesh(FiniteElement("Lagrange", cell, 1, (d,), identity_pullback, H1))
v0_space = FunctionSpace(domain, V0)
v1_space = FunctionSpace(domain, V1)
@@ -50,7 +75,7 @@ def test_apply_derivatives_doesnt_change_expression_without_derivatives():
# Expressions
e0 = f0 + f1
- e1 = v0 * (f1/3 - f0**2)
+ e1 = v0 * (f1 / 3 - f0**2)
e2 = exp(sin(cos(tan(ln(x[0])))))
expressions = [e0, e1, e2]
@@ -63,7 +88,7 @@ def test_apply_derivatives_doesnt_change_expression_without_derivatives():
def test_literal_derivatives_are_zero():
cell = triangle
- d = cell.geometric_dimension()
+ d = 2
# Literals
one = as_ufl(1)
@@ -73,7 +98,7 @@ def test_literal_derivatives_are_zero():
# Generic ruleset handles literals directly:
for lit in literals:
- for sh in [(), (d,), (d, d+1)]:
+ for sh in [(), (d,), (d, d + 1)]:
assert GenericDerivativeRuleset(sh)(lit) == zero(lit.ufl_shape + sh)
# Variables
@@ -89,7 +114,7 @@ def test_literal_derivatives_are_zero():
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))
+ domain = Mesh(FiniteElement("Lagrange", cell, 1, (d,), identity_pullback, H1))
v0_space = FunctionSpace(domain, V0)
v1_space = FunctionSpace(domain, V1)
u0 = Coefficient(v0_space)
@@ -110,16 +135,16 @@ def test_literal_derivatives_are_zero():
def test_grad_ruleset():
cell = triangle
- d = cell.geometric_dimension()
+ d = 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)
+ 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(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1))
+ 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)
@@ -197,10 +222,14 @@ def test_grad_ruleset():
assert renumber_indices(apply_derivatives(grad(vf2[1])[0])) == renumber_indices(grad(vf2)[1, 0])
# Grad of gradually more complex expressions
- assert apply_derivatives(grad(2*f0)) == zero((d,))
- assert renumber_indices(apply_derivatives(grad(2*f1))) == renumber_indices(2*grad(f1))
- assert renumber_indices(apply_derivatives(grad(sin(f1)))) == renumber_indices(cos(f1) * grad(f1))
- assert renumber_indices(apply_derivatives(grad(cos(f1)))) == renumber_indices(-sin(f1) * grad(f1))
+ assert apply_derivatives(grad(2 * f0)) == zero((d,))
+ assert renumber_indices(apply_derivatives(grad(2 * f1))) == renumber_indices(2 * grad(f1))
+ assert renumber_indices(apply_derivatives(grad(sin(f1)))) == renumber_indices(
+ cos(f1) * grad(f1)
+ )
+ assert renumber_indices(apply_derivatives(grad(cos(f1)))) == renumber_indices(
+ -sin(f1) * grad(f1)
+ )
def test_variable_ruleset():
diff --git a/test/test_pickle.py b/test/test_pickle.py
index 5ab4026f7..e2611c48a 100755
--- a/test/test_pickle.py
+++ b/test/test_pickle.py
@@ -10,9 +10,37 @@
import pickle
-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 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
@@ -23,7 +51,7 @@
def testConstant():
element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
- domain = Mesh(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)
@@ -47,8 +75,8 @@ def testConstant():
def testElasticity():
- element = FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1))
+ 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)
@@ -69,7 +97,7 @@ def eps(v):
def testEnergyNorm():
element = FiniteElement("Lagrange", tetrahedron, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3,), identity_pullback, H1))
space = FunctionSpace(domain, element)
v = Coefficient(space)
@@ -83,7 +111,7 @@ def testEnergyNorm():
def testEquation():
element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
space = FunctionSpace(domain, element)
k = 0.1
@@ -108,7 +136,7 @@ def testEquation():
def testFunctionOperators():
element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
- domain = Mesh(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)
@@ -118,8 +146,7 @@ def testFunctionOperators():
# FFC notation: a = sqrt(1/modulus(1/f))*sqrt(g)*dot(grad(v), grad(u))*dx
# + v*u*sqrt(f*g)*g*dx
- a = sqrt(1 / abs(1 / f)) * sqrt(g) * \
- dot(grad(v), grad(u)) * dx + v * u * sqrt(f * g) * g * dx
+ a = sqrt(1 / abs(1 / f)) * sqrt(g) * dot(grad(v), grad(u)) * dx + v * u * sqrt(f * g) * g * dx
a_pickle = pickle.dumps(a, p)
a_restore = pickle.loads(a_pickle)
@@ -129,7 +156,7 @@ def testFunctionOperators():
def testHeat():
element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
- domain = Mesh(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)
@@ -153,7 +180,7 @@ def testHeat():
def testMass():
element = FiniteElement("Lagrange", tetrahedron, 3, (), identity_pullback, H1)
- domain = Mesh(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)
@@ -181,11 +208,11 @@ def testMixedMixedElement():
def testMixedPoisson():
q = 1
- BDM = FiniteElement("Brezzi-Douglas-Marini", triangle, q, (2, ), contravariant_piola, HDiv)
+ BDM = FiniteElement("Brezzi-Douglas-Marini", triangle, q, (2,), contravariant_piola, HDiv)
DG = FiniteElement("Discontinuous Lagrange", triangle, q - 1, (), identity_pullback, L2)
mixed_element = MixedElement([BDM, DG])
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
mixed_space = FunctionSpace(domain, mixed_element)
dg_space = FunctionSpace(domain, DG)
@@ -207,8 +234,8 @@ def testMixedPoisson():
def testNavierStokes():
- element = FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1))
+ 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)
@@ -226,8 +253,8 @@ def testNavierStokes():
def testNeumannProblem():
- element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)
@@ -252,7 +279,7 @@ def testNeumannProblem():
def testOptimization():
element = FiniteElement("Lagrange", triangle, 3, (), identity_pullback, H1)
- domain = Mesh(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)
@@ -289,7 +316,7 @@ def testP5tri():
def testPoissonDG():
element = FiniteElement("Discontinuous Lagrange", triangle, 1, (), identity_pullback, L2)
- domain = Mesh(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)
@@ -315,13 +342,15 @@ def testPoissonDG():
# - dot(mult(v,n), grad(u))*ds \
# + gamma/h*v*u*ds
- a = inner(grad(v), grad(u)) * dx \
- - inner(avg(grad(v)), jump(u, n)) * dS \
- - inner(jump(v, n), avg(grad(u))) * dS \
- + alpha / h('+') * dot(jump(v, n), jump(u, n)) * dS \
- - inner(grad(v), u * n) * ds \
- - inner(u * n, grad(u)) * ds \
+ a = (
+ inner(grad(v), grad(u)) * dx
+ - inner(avg(grad(v)), jump(u, n)) * dS
+ - inner(jump(v, n), avg(grad(u))) * dS
+ + alpha / h("+") * dot(jump(v, n), jump(u, n)) * dS
+ - inner(grad(v), u * n) * ds
+ - inner(u * n, grad(u)) * ds
+ gamma / h * v * u * ds
+ )
L = v * f * dx + v * gN * ds
@@ -336,7 +365,7 @@ def testPoissonDG():
def testPoisson():
element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
- domain = Mesh(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)
@@ -357,8 +386,8 @@ def testPoisson():
def testPoissonSystem():
- element = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)
@@ -388,9 +417,9 @@ def testQuadratureElement():
# sig = VectorQuadratureElement(triangle, 3)
QE = FiniteElement("Quadrature", triangle, 3, (), identity_pullback, L2)
- sig = FiniteElement("Quadrature", triangle, 3, (2, ), identity_pullback, L2)
+ sig = FiniteElement("Quadrature", triangle, 3, (2,), identity_pullback, L2)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
space = FunctionSpace(domain, element)
qe_space = FunctionSpace(domain, QE)
sig_space = FunctionSpace(domain, sig)
@@ -417,11 +446,11 @@ def testQuadratureElement():
def testStokes():
# UFLException: Shape mismatch in sum.
- P2 = FiniteElement("Lagrange", triangle, 2, (2, ), identity_pullback, H1)
+ P2 = FiniteElement("Lagrange", triangle, 2, (2,), identity_pullback, H1)
P1 = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
TH = MixedElement([P2, P1])
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
th_space = FunctionSpace(domain, TH)
p2_space = FunctionSpace(domain, P2)
@@ -447,7 +476,7 @@ def testStokes():
def testSubDomain():
element = FiniteElement("Lagrange", tetrahedron, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3,), identity_pullback, H1))
space = FunctionSpace(domain, element)
f = Coefficient(space)
@@ -462,14 +491,20 @@ def testSubDomain():
def testSubDomains():
element = FiniteElement("Lagrange", tetrahedron, 1, (), identity_pullback, H1)
- domain = Mesh(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)
u = TrialFunction(space)
- a = v * u * dx(0) + 10.0 * v * u * dx(1) + v * u * ds(0) + 2.0 * v * u * \
- ds(1) + v('+') * u('+') * dS(0) + 4.3 * v('+') * u('+') * dS(1)
+ a = (
+ v * u * dx(0)
+ + 10.0 * v * u * dx(1)
+ + v * u * ds(0)
+ + 2.0 * v * u * ds(1)
+ + v("+") * u("+") * dS(0)
+ + 4.3 * v("+") * u("+") * dS(1)
+ )
a_pickle = pickle.dumps(a, p)
a_restore = pickle.loads(a_pickle)
@@ -498,7 +533,7 @@ def testTensorWeightedPoisson():
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))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
p1_space = FunctionSpace(domain, P1)
p0_space = FunctionSpace(domain, P0)
@@ -522,8 +557,12 @@ def HodgeLaplaceGradCurl(space, fspace):
# FFC notation: a = (dot(tau, sigma) - dot(grad(tau), u) + dot(v,
# grad(sigma)) + dot(curl(v), curl(u)))*dx
- a = (inner(tau, sigma) - inner(grad(tau), u) +
- inner(v, grad(sigma)) + inner(curl(v), curl(u))) * dx
+ a = (
+ inner(tau, sigma)
+ - inner(grad(tau), u)
+ + inner(v, grad(sigma))
+ + inner(curl(v), curl(u))
+ ) * dx
# FFC notation: L = dot(v, f)*dx
L = inner(v, f) * dx
@@ -536,13 +575,14 @@ def HodgeLaplaceGradCurl(space, fspace):
GRAD = FiniteElement("Lagrange", shape, order, (), identity_pullback, H1)
# FFC notation: CURL = FiniteElement("Nedelec", shape, order-1)
- CURL = FiniteElement("N1curl", shape, order, (3, ), covariant_piola, HCurl)
+ CURL = FiniteElement("N1curl", shape, order, (3,), covariant_piola, HCurl)
- VectorLagrange = FiniteElement("Lagrange", shape, order + 1, (3, ), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", shape, 1, (3, ), identity_pullback, H1))
+ 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, MixedElement([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)
@@ -562,7 +602,7 @@ def testIdentity():
def testFormData():
element = FiniteElement("Lagrange", tetrahedron, 3, (), identity_pullback, H1)
- domain = Mesh(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/test/test_piecewise_checks.py b/test/test_piecewise_checks.py
index d5d419d37..baeb13c59 100755
--- a/test/test_piecewise_checks.py
+++ b/test/test_piecewise_checks.py
@@ -2,12 +2,37 @@
import pytest
-from ufl import (Cell, CellDiameter, CellVolume, Circumradius, Coefficient, Constant, FacetArea, FacetNormal,
- FunctionSpace, Jacobian, JacobianDeterminant, JacobianInverse, MaxFacetEdgeLength, Mesh,
- MinFacetEdgeLength, SpatialCoordinate, TestFunction, hexahedron, interval, quadrilateral, tetrahedron,
- triangle)
+from ufl import (
+ Cell,
+ CellDiameter,
+ CellVolume,
+ Circumradius,
+ Coefficient,
+ Constant,
+ FacetArea,
+ FacetNormal,
+ 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.classes import (
+ CellCoordinate,
+ FacetJacobian,
+ FacetJacobianDeterminant,
+ FacetJacobianInverse,
+)
from ufl.finiteelement import FiniteElement
from ufl.pullback import identity_pullback
from ufl.sobolevspace import H1, L2, HInf
@@ -22,15 +47,27 @@ def get_domains():
tetrahedron,
hexahedron,
]
- return [Mesh(FiniteElement("Lagrange", cell, 1, (cell.geometric_dimension(), ),
- identity_pullback, H1)) for cell in all_cells]
+ return [
+ Mesh(
+ FiniteElement(
+ "Lagrange", cell, 1, (cell.topological_dimension(),), identity_pullback, H1
+ )
+ )
+ for cell in all_cells
+ ]
def get_nonlinear():
domains_with_quadratic_coordinates = []
for D in get_domains():
- V = FiniteElement("Lagrange", D.ufl_cell(), 2, (D.ufl_cell().geometric_dimension(), ),
- identity_pullback, H1)
+ V = FiniteElement(
+ "Lagrange",
+ D.ufl_cell(),
+ 2,
+ (D.ufl_cell().topological_dimension(),),
+ identity_pullback,
+ H1,
+ )
E = Mesh(V)
domains_with_quadratic_coordinates.append(E)
@@ -53,8 +90,14 @@ def domains(request):
domains = get_domains()
domains_with_linear_coordinates = []
for D in domains:
- V = FiniteElement("Lagrange", D.ufl_cell(), 1, (D.ufl_cell().geometric_dimension(), ),
- identity_pullback, H1)
+ V = FiniteElement(
+ "Lagrange",
+ D.ufl_cell(),
+ 1,
+ (D.ufl_cell().topological_dimension(),),
+ identity_pullback,
+ H1,
+ )
E = Mesh(V)
domains_with_linear_coordinates.append(E)
@@ -69,19 +112,29 @@ def affine_domains(request):
triangle,
tetrahedron,
]
- affine_domains = [Mesh(FiniteElement("Lagrange", cell, 1, (cell.geometric_dimension(), ),
- identity_pullback, H1))
- for cell in affine_cells]
+ affine_domains = [
+ Mesh(
+ FiniteElement(
+ "Lagrange", cell, 1, (cell.topological_dimension(),), identity_pullback, H1
+ )
+ )
+ for cell in affine_cells
+ ]
affine_domains_with_linear_coordinates = []
for D in affine_domains:
- V = FiniteElement("Lagrange", D.ufl_cell(), 1, (D.ufl_cell().geometric_dimension(), ),
- identity_pullback, H1)
+ V = FiniteElement(
+ "Lagrange",
+ D.ufl_cell(),
+ 1,
+ (D.ufl_cell().topological_dimension(),),
+ identity_pullback,
+ H1,
+ )
E = Mesh(V)
affine_domains_with_linear_coordinates.append(E)
- all_affine_domains = affine_domains + \
- affine_domains_with_linear_coordinates
+ all_affine_domains = affine_domains + affine_domains_with_linear_coordinates
return all_affine_domains[request.param]
@@ -93,18 +146,28 @@ def affine_facet_domains(request):
quadrilateral,
tetrahedron,
]
- affine_facet_domains = [Mesh(FiniteElement(
- "Lagrange", cell, 1, (cell.geometric_dimension(), ),
- identity_pullback, H1)) for cell in affine_facet_cells]
+ affine_facet_domains = [
+ Mesh(
+ FiniteElement(
+ "Lagrange", cell, 1, (cell.topological_dimension(),), identity_pullback, H1
+ )
+ )
+ for cell in affine_facet_cells
+ ]
affine_facet_domains_with_linear_coordinates = []
for D in affine_facet_domains:
- V = FiniteElement("Lagrange", D.ufl_cell(), 1, (D.ufl_cell().geometric_dimension(), ),
- identity_pullback, H1)
+ V = FiniteElement(
+ "Lagrange",
+ D.ufl_cell(),
+ 1,
+ (D.ufl_cell().topological_dimension(),),
+ identity_pullback,
+ H1,
+ )
E = Mesh(V)
affine_facet_domains_with_linear_coordinates.append(E)
- all_affine_facet_domains = affine_facet_domains + \
- affine_facet_domains_with_linear_coordinates
+ all_affine_facet_domains = affine_facet_domains + affine_facet_domains_with_linear_coordinates
return all_affine_facet_domains[request.param]
@@ -115,18 +178,28 @@ def nonaffine_domains(request):
quadrilateral,
hexahedron,
]
- nonaffine_domains = [Mesh(FiniteElement(
- "Lagrange", cell, 1, (cell.geometric_dimension(), ),
- identity_pullback, H1)) for cell in nonaffine_cells]
+ nonaffine_domains = [
+ Mesh(
+ FiniteElement(
+ "Lagrange", cell, 1, (cell.topological_dimension(),), identity_pullback, H1
+ )
+ )
+ for cell in nonaffine_cells
+ ]
nonaffine_domains_with_linear_coordinates = []
for D in nonaffine_domains:
- V = FiniteElement("Lagrange", D.ufl_cell(), 1, (D.ufl_cell().geometric_dimension(), ),
- identity_pullback, H1)
+ V = FiniteElement(
+ "Lagrange",
+ D.ufl_cell(),
+ 1,
+ (D.ufl_cell().topological_dimension(),),
+ identity_pullback,
+ H1,
+ )
E = Mesh(V)
nonaffine_domains_with_linear_coordinates.append(E)
- all_nonaffine_domains = nonaffine_domains + \
- nonaffine_domains_with_linear_coordinates
+ all_nonaffine_domains = nonaffine_domains + nonaffine_domains_with_linear_coordinates
return all_nonaffine_domains[request.param]
@@ -136,18 +209,30 @@ def nonaffine_facet_domains(request):
nonaffine_facet_cells = [
hexahedron,
]
- nonaffine_facet_domains = [Mesh(FiniteElement(
- "Lagrange", cell, 1, (cell.geometric_dimension(), ),
- identity_pullback, H1)) for cell in nonaffine_facet_cells]
+ nonaffine_facet_domains = [
+ Mesh(
+ FiniteElement(
+ "Lagrange", cell, 1, (cell.topological_dimension(),), identity_pullback, H1
+ )
+ )
+ for cell in nonaffine_facet_cells
+ ]
nonaffine_facet_domains_with_linear_coordinates = []
for D in nonaffine_facet_domains:
- V = FiniteElement("Lagrange", D.ufl_cell(), 1, (D.ufl_cell().geometric_dimension(), ),
- identity_pullback, H1)
+ V = FiniteElement(
+ "Lagrange",
+ D.ufl_cell(),
+ 1,
+ (D.ufl_cell().topological_dimension(),),
+ identity_pullback,
+ H1,
+ )
E = Mesh(V)
nonaffine_facet_domains_with_linear_coordinates.append(E)
- all_nonaffine_facet_domains = nonaffine_facet_domains + \
- nonaffine_facet_domains_with_linear_coordinates
+ all_nonaffine_facet_domains = (
+ nonaffine_facet_domains + nonaffine_facet_domains_with_linear_coordinates
+ )
return all_nonaffine_facet_domains[request.param]
@@ -177,7 +262,7 @@ def test_coordinates_never_cellwise_constant(domains):
def test_coordinates_never_cellwise_constant_vertex():
# The only exception here:
- domains = Mesh(FiniteElement("Lagrange", Cell("vertex", 3), 1, (3, ), identity_pullback, H1))
+ domains = Mesh(FiniteElement("Lagrange", Cell("vertex"), 1, (3,), identity_pullback, H1))
assert domains.ufl_cell().cellname() == "vertex"
e = SpatialCoordinate(domains)
assert is_cellwise_constant(e)
@@ -234,9 +319,13 @@ def test_coefficient_sometimes_cellwise_constant(domains_not_linear):
e = Constant(domains_not_linear)
assert is_cellwise_constant(e)
- 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))
+ V = FiniteElement(
+ "Discontinuous Lagrange", domains_not_linear.ufl_cell(), 0, (), identity_pullback, L2
+ )
+ d = domains_not_linear.ufl_cell().topological_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)
@@ -255,9 +344,13 @@ def test_coefficient_sometimes_cellwise_constant(domains_not_linear):
def test_coefficient_mostly_not_cellwise_constant(domains_not_linear):
- 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))
+ V = FiniteElement(
+ "Discontinuous Lagrange", domains_not_linear.ufl_cell(), 1, (), identity_pullback, L2
+ )
+ d = domains_not_linear.ufl_cell().topological_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 12c5028be..d5aec17a3 100755
--- a/test/test_reference_shapes.py
+++ b/test/test_reference_shapes.py
@@ -1,40 +1,59 @@
-from ufl import Cell
+from ufl import Cell, Mesh
from ufl.finiteelement import FiniteElement, MixedElement, SymmetricElement
+from ufl.functionspace import FunctionSpace
from ufl.pullback import contravariant_piola, covariant_piola, identity_pullback
from ufl.sobolevspace import H1, HCurl, HDiv
def test_reference_shapes():
# show_elements()
+ cell = Cell("triangle")
+ domain = Mesh(FiniteElement("Lagrange", cell, 1, (3,), identity_pullback, H1))
- cell = Cell("triangle", 3)
-
- V = FiniteElement("N1curl", cell, 1, (2, ), covariant_piola, HCurl)
- assert V.value_shape == (3,)
+ V = FiniteElement("N1curl", cell, 1, (2,), covariant_piola, HCurl)
+ Vspace = FunctionSpace(domain, V)
+ assert Vspace.value_shape == (3,)
assert V.reference_value_shape == (2,)
- U = FiniteElement("Raviart-Thomas", cell, 1, (2, ), contravariant_piola, HDiv)
- assert U.value_shape == (3,)
+ U = FiniteElement("Raviart-Thomas", cell, 1, (2,), contravariant_piola, HDiv)
+ Uspace = FunctionSpace(domain, U)
+ assert Uspace.value_shape == (3,)
assert U.reference_value_shape == (2,)
W = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1)
- assert W.value_shape == ()
+ Wspace = FunctionSpace(domain, W)
+ assert Wspace.value_shape == ()
assert W.reference_value_shape == ()
- Q = FiniteElement("Lagrange", cell, 1, (3, ), identity_pullback, H1)
- assert Q.value_shape == (3,)
+ Q = FiniteElement("Lagrange", cell, 1, (3,), identity_pullback, H1)
+ Qspace = FunctionSpace(domain, Q)
+ assert Qspace.value_shape == (3,)
assert Q.reference_value_shape == (3,)
T = FiniteElement("Lagrange", cell, 1, (3, 3), identity_pullback, H1)
- assert T.value_shape == (3, 3)
+ Tspace = FunctionSpace(domain, T)
+ assert Tspace.value_shape == (3, 3)
assert T.reference_value_shape == (3, 3)
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)
+ {
+ (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)],
+ )
+ Sspace = FunctionSpace(domain, S)
+ assert Sspace.value_shape == (3, 3)
assert S.reference_value_shape == (6,)
M = MixedElement([V, U, W])
- assert M.value_shape == (7,)
+ Mspace = FunctionSpace(domain, M)
+ assert Mspace.value_shape == (7,)
assert M.reference_value_shape == (5,)
diff --git a/test/test_scratch.py b/test/test_scratch.py
index 32ced3346..5b429e532 100755
--- a/test/test_scratch.py
+++ b/test/test_scratch.py
@@ -8,8 +8,22 @@
import warnings
-from ufl import (Coefficient, FunctionSpace, Identity, Mesh, TestFunction, 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
@@ -18,15 +32,14 @@
class MockForwardAD:
-
def __init__(self):
self._w = ()
self._v = ()
class Obj:
-
def __init__(self):
self._data = {}
+
self._cd = Obj()
def grad(self, g):
@@ -39,25 +52,25 @@ def grad(self, g):
ngrads = 0
o = g
while isinstance(o, Grad):
- o, = o.ufl_operands
+ (o,) = o.ufl_operands
ngrads += 1
if not isinstance(o, FormArgument):
raise ValueError("Expecting gradient of a FormArgument, not %s" % repr(o))
def apply_grads(f):
if not isinstance(f, FormArgument):
- print((','*60))
+ print(("," * 60))
print(f)
print(o)
print(g)
- print((','*60))
+ print(("," * 60))
raise ValueError("What?")
for i in range(ngrads):
f = Grad(f)
return f
# Find o among all w without any indexing, which makes this easy
- for (w, v) in zip(self._w, self._v):
+ for w, v in zip(self._w, self._v):
if o == w and isinstance(v, FormArgument):
# Case: d/dt [w + t v]
return (g, apply_grads(v))
@@ -85,18 +98,17 @@ def compute_gprimeterm(ngrads, vval, vcomp, wshape, wcomp):
# Apply gradients directly to argument vval,
# and get the right indexed scalar component(s)
kk = indices(ngrads)
- Dvkk = apply_grads(vval)[vcomp+kk]
+ Dvkk = apply_grads(vval)[vcomp + kk]
# Place scalar component(s) Dvkk into the right tensor positions
if wshape:
Ejj, jj = unit_indexed_tensor(wshape, wcomp)
else:
Ejj, jj = 1, ()
- gprimeterm = as_tensor(Ejj*Dvkk, jj+kk)
+ gprimeterm = as_tensor(Ejj * Dvkk, jj + kk)
return gprimeterm
# Accumulate contributions from variations in different components
- for (w, v) in zip(self._w, self._v):
-
+ for w, v in zip(self._w, self._v):
# Analyse differentiation variable coefficient
if isinstance(w, FormArgument):
if not w == o:
@@ -112,7 +124,9 @@ def compute_gprimeterm(ngrads, vval, vcomp, wshape, wcomp):
for wcomp, vsub in unwrap_list_tensor(v):
if not isinstance(vsub, Zero):
vval, vcomp = analyse_variation_argument(vsub)
- gprimesum = gprimesum + compute_gprimeterm(ngrads, vval, vcomp, wshape, wcomp)
+ gprimesum = gprimesum + compute_gprimeterm(
+ ngrads, vval, vcomp, wshape, wcomp
+ )
else:
if wshape != ():
@@ -123,7 +137,9 @@ def compute_gprimeterm(ngrads, vval, vcomp, wshape, wcomp):
vval, vcomp = analyse_variation_argument(v)
gprimesum = gprimesum + compute_gprimeterm(ngrads, vval, vcomp, wshape, wcomp)
- elif isinstance(w, Indexed): # This path is tested in unit tests, but not actually used?
+ elif isinstance(
+ w, Indexed
+ ): # This path is tested in unit tests, but not actually used?
# Case: d/dt [w[...] + t v[...]]
# Case: d/dt [w[...] + t v]
wval, wcomp = w.ufl_operands
@@ -154,20 +170,22 @@ def compute_gprimeterm(ngrads, vval, vcomp, wshape, wcomp):
if not isinstance(oprimes, tuple):
oprimes = (oprimes,)
if len(oprimes) != len(self._v):
- raise ValueError("Got a tuple of arguments, "
- "expecting a matching tuple of coefficient derivatives.")
+ raise ValueError(
+ "Got a tuple of arguments, "
+ "expecting a matching tuple of coefficient derivatives."
+ )
# Compute dg/dw_j = dg/dw_h : v.
# Since we may actually have a tuple of oprimes and vs in a
# 'mixed' space, sum over them all to get the complete inner
# product. Using indices to define a non-compound inner product.
- for (oprime, v) in zip(oprimes, self._v):
+ for oprime, v in zip(oprimes, self._v):
raise ValueError("FIXME: Figure out how to do this with ngrads")
so, oi = as_scalar(oprime)
rv = len(v.ufl_shape)
oi1 = oi[:-rv]
oi2 = oi[-rv:]
- prod = so*v[oi2]
+ prod = so * v[oi2]
if oi1:
gprimesum += as_tensor(prod, oi1)
else:
@@ -185,36 +203,41 @@ def test_unit_tensor(self):
def test_unwrap_list_tensor(self):
lt = as_tensor((1, 2))
- expected = [((0,), 1),
- ((1,), 2), ]
+ expected = [
+ ((0,), 1),
+ ((1,), 2),
+ ]
comp = unwrap_list_tensor(lt)
assert comp == expected
lt = as_tensor(((1, 2), (3, 4)))
- expected = [((0, 0), 1),
- ((0, 1), 2),
- ((1, 0), 3),
- ((1, 1), 4), ]
+ expected = [
+ ((0, 0), 1),
+ ((0, 1), 2),
+ ((1, 0), 3),
+ ((1, 1), 4),
+ ]
comp = unwrap_list_tensor(lt)
assert comp == expected
- lt = as_tensor((((1, 2), (3, 4)),
- ((11, 12), (13, 14))))
- expected = [((0, 0, 0), 1),
- ((0, 0, 1), 2),
- ((0, 1, 0), 3),
- ((0, 1, 1), 4),
- ((1, 0, 0), 11),
- ((1, 0, 1), 12),
- ((1, 1, 0), 13),
- ((1, 1, 1), 14), ]
+ lt = as_tensor((((1, 2), (3, 4)), ((11, 12), (13, 14))))
+ expected = [
+ ((0, 0, 0), 1),
+ ((0, 0, 1), 2),
+ ((0, 1, 0), 3),
+ ((0, 1, 1), 4),
+ ((1, 0, 0), 11),
+ ((1, 0, 1), 12),
+ ((1, 1, 0), 13),
+ ((1, 1, 1), 14),
+ ]
comp = unwrap_list_tensor(lt)
assert comp == expected
def test__forward_coefficient_ad__grad_of_scalar_coefficient(self):
U = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
space = FunctionSpace(domain, U)
u = Coefficient(space)
du = TestFunction(space)
@@ -239,8 +262,8 @@ def test__forward_coefficient_ad__grad_of_scalar_coefficient(self):
def test__forward_coefficient_ad__grad_of_vector_coefficient(self):
- V = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)
@@ -265,8 +288,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 = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)
@@ -280,19 +303,20 @@ def test__forward_coefficient_ad__grad_of_vector_coefficient__with_component_var
f = grad(v)
df = grad(as_vector((dv[1], 0))) # Mathematically this would be the natural result
j, k = indices(2)
- df = as_tensor(Identity(2)[0, j]*grad(dv)[1, k], (j, k)) # Actual representation should have grad right next to dv
+ df = as_tensor(
+ Identity(2)[0, j] * grad(dv)[1, k], (j, k)
+ ) # Actual representation should have grad right next to dv
g, dg = mad.grad(f)
if 0:
- print(('\nf ', f))
- print(('df ', df))
- print(('g ', g))
- print(('dg ', dg))
+ print(("\nf ", f))
+ print(("df ", df))
+ print(("g ", g))
+ print(("dg ", dg))
assert f.ufl_shape == df.ufl_shape
assert g.ufl_shape == f.ufl_shape
assert dg.ufl_shape == df.ufl_shape
assert g == f
- self.assertEqual((inner(dg, dg)*dx).signature(),
- (inner(df, df)*dx).signature())
+ self.assertEqual((inner(dg, dg) * dx).signature(), (inner(df, df) * dx).signature())
# assert dg == df # Expected to fail because of different index numbering
# Multiple components of variation:
@@ -305,25 +329,27 @@ def test__forward_coefficient_ad__grad_of_vector_coefficient__with_component_var
# Actual representation should have grad right next to dv:
j0, k0 = indices(2)
j1, k1 = indices(2) # Using j0,k0 for both terms gives different signature
- df = (as_tensor(Identity(2)[0, j0]*grad(dv)[1, k0], (j0, k0))
- + as_tensor(Identity(2)[1, j1]*grad(dv)[0, k1], (j1, k1)))
+ df = as_tensor(Identity(2)[0, j0] * grad(dv)[1, k0], (j0, k0)) + as_tensor(
+ Identity(2)[1, j1] * grad(dv)[0, k1], (j1, k1)
+ )
g, dg = mad.grad(f)
- print(('\nf ', f))
- print(('df ', df))
- print(('g ', g))
- print(('dg ', dg))
+ print(("\nf ", f))
+ print(("df ", df))
+ print(("g ", g))
+ print(("dg ", dg))
assert f.ufl_shape == df.ufl_shape
assert g.ufl_shape == f.ufl_shape
assert dg.ufl_shape == df.ufl_shape
assert g == f
- self.assertEqual((inner(dg, dg)*dx).signature(),
- (inner(df, df)*dx).signature())
+ self.assertEqual((inner(dg, dg) * dx).signature(), (inner(df, df) * dx).signature())
# assert dg == df # Expected to fail because of different index numbering
-def test__forward_coefficient_ad__grad_of_vector_coefficient__with_component_variation_in_list(self):
- V = FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+def test__forward_coefficient_ad__grad_of_vector_coefficient__with_component_variation_in_list(
+ self,
+):
+ 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)
@@ -337,24 +363,25 @@ def test__forward_coefficient_ad__grad_of_vector_coefficient__with_component_var
f = grad(v)
df = grad(as_vector((dv[1], 0))) # Mathematically this would be the natural result
j, k = indices(2)
- df = as_tensor(Identity(2)[0, j]*grad(dv)[1, k], (j, k)) # Actual representation should have grad right next to dv
+ df = as_tensor(
+ Identity(2)[0, j] * grad(dv)[1, k], (j, k)
+ ) # Actual representation should have grad right next to dv
g, dg = mad.grad(f)
if 0:
- print(('\nf ', f))
- print(('df ', df))
- print(('g ', g))
- print(('dg ', dg))
+ print(("\nf ", f))
+ print(("df ", df))
+ print(("g ", g))
+ print(("dg ", dg))
assert f.ufl_shape == df.ufl_shape
assert g.ufl_shape == f.ufl_shape
assert dg.ufl_shape == df.ufl_shape
assert g == f
- self.assertEqual((inner(dg, dg)*dx).signature(),
- (inner(df, df)*dx).signature())
+ self.assertEqual((inner(dg, dg) * dx).signature(), (inner(df, df) * dx).signature())
# assert dg == df # Expected to fail because of different index numbering
# Multiple components of variation:
# grad(grad(c))[0,1,:,:] -> grad(grad(dc))[1,0,:,:]
- mad._w = (v, )
+ mad._w = (v,)
mad._v = (as_vector((dv[1], dv[0])),)
f = grad(v)
# Mathematically this would be the natural result:
@@ -362,25 +389,25 @@ def test__forward_coefficient_ad__grad_of_vector_coefficient__with_component_var
# Actual representation should have grad right next to dv:
j0, k0 = indices(2)
j1, k1 = indices(2) # Using j0,k0 for both terms gives different signature
- df = (as_tensor(Identity(2)[0, j0]*grad(dv)[1, k0], (j0, k0))
- + as_tensor(Identity(2)[1, j1]*grad(dv)[0, k1], (j1, k1)))
+ df = as_tensor(Identity(2)[0, j0] * grad(dv)[1, k0], (j0, k0)) + as_tensor(
+ Identity(2)[1, j1] * grad(dv)[0, k1], (j1, k1)
+ )
g, dg = mad.grad(f)
- print(('\nf ', f))
- print(('df ', df))
- print(('g ', g))
- print(('dg ', dg))
+ print(("\nf ", f))
+ print(("df ", df))
+ print(("g ", g))
+ print(("dg ", dg))
assert f.ufl_shape == df.ufl_shape
assert g.ufl_shape == f.ufl_shape
assert dg.ufl_shape == df.ufl_shape
assert g == f
- self.assertEqual((inner(dg, dg)*dx).signature(),
- (inner(df, df)*dx).signature())
+ self.assertEqual((inner(dg, dg) * dx).signature(), (inner(df, df) * dx).signature())
# assert dg == df # Expected to fail because of different index numbering
def test__forward_coefficient_ad__grad_of_tensor_coefficient(self):
W = FiniteElement("Lagrange", triangle, 1, (2, 2), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
space = FunctionSpace(domain, W)
w = Coefficient(space)
dw = TestFunction(space)
@@ -406,7 +433,7 @@ def test__forward_coefficient_ad__grad_of_tensor_coefficient(self):
def test__forward_coefficient_ad__grad_of_tensor_coefficient__with_component_variation(self):
W = FiniteElement("Lagrange", triangle, 1, (2, 2), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
space = FunctionSpace(domain, W)
w = Coefficient(space)
dw = TestFunction(space)
@@ -424,17 +451,16 @@ def test__forward_coefficient_ad__grad_of_tensor_coefficient__with_component_var
i, j, k = indices(3)
E = outer(Identity(2)[wc[0], i], Identity(2)[wc[1], j])
Ddw = grad(dw)[dwc + (k,)]
- df = as_tensor(E*Ddw, (i, j, k)) # Actual representation should have grad next to dv
+ df = as_tensor(E * Ddw, (i, j, k)) # Actual representation should have grad next to dv
g, dg = mad.grad(f)
if 0:
- print(('\nf ', f))
- print(('df ', df))
- print(('g ', g))
- print(('dg ', dg))
+ print(("\nf ", f))
+ print(("df ", df))
+ print(("g ", g))
+ print(("dg ", dg))
assert f.ufl_shape == df.ufl_shape
assert g.ufl_shape == f.ufl_shape
assert dg.ufl_shape == df.ufl_shape
assert g == f
- self.assertEqual((inner(dg, dg)*dx).signature(),
- (inner(df, df)*dx).signature())
+ self.assertEqual((inner(dg, dg) * dx).signature(), (inner(df, df) * dx).signature())
# assert dg == df # Expected to fail because of different index numbering
diff --git a/test/test_signature.py b/test/test_signature.py
index 4e270d500..aa8c57877 100755
--- a/test/test_signature.py
+++ b/test/test_signature.py
@@ -1,8 +1,32 @@
"""Test the computation of form signatures."""
-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 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
@@ -21,8 +45,8 @@
def domain_numbering(*cells):
renumbering = {}
for i, cell in enumerate(cells):
- d = cell.geometric_dimension()
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1), ufl_id=i)
+ d = cell.topological_dimension()
+ domain = Mesh(FiniteElement("Lagrange", cell, 1, (d,), identity_pullback, H1), ufl_id=i)
renumbering[domain] = i
return renumbering
@@ -60,12 +84,14 @@ def test_terminal_hashdata_depends_on_literals(self):
def forms():
i, j = indices(2)
for d, cell in [(2, triangle), (3, tetrahedron)]:
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1), 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):
for iv in (5, 7):
- expr = (ident[0, j]*(fv*x[j]))**iv
+ expr = (ident[0, j] * (fv * x[j])) ** iv
reprs.add(repr(expr))
hashes.add(hash(expr))
@@ -88,8 +114,8 @@ def forms():
i, j = indices(2)
cells = (triangle, tetrahedron)
for i, cell in enumerate(cells):
- d = cell.geometric_dimension()
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1), ufl_id=i)
+ d = cell.topological_dimension()
+ domain = Mesh(FiniteElement("Lagrange", cell, 1, (d,), identity_pullback, H1), ufl_id=i)
x = SpatialCoordinate(domain)
n = FacetNormal(domain)
@@ -104,14 +130,14 @@ def forms():
qs = (h, r, a, v) # , s)
for w in ws:
for q in qs:
- expr = (ident[0, j]*(q*w[j]))
+ expr = ident[0, j] * (q * w[j])
reprs.add(repr(expr))
hashes.add(hash(expr))
yield compute_terminal_hashdata(expr, domain_numbering(*cells))
c, d, r, h = compute_unique_terminal_hashdatas(forms())
- assert c == 2*4*2 # len(ws)*len(qs)*len(cells)
+ assert c == 2 * 4 * 2 # len(ws)*len(qs)*len(cells)
assert d == c
assert r == c
assert h == c
@@ -133,26 +159,49 @@ def test_terminal_hashdata_depends_on_form_argument_properties(self):
def forms():
for rep in range(nreps):
for i, cell in enumerate(cells):
- d = cell.geometric_dimension()
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1), ufl_id=i)
+ d = cell.topological_dimension()
+ domain = Mesh(
+ FiniteElement("Lagrange", cell, 1, (d,), identity_pullback, H1), ufl_id=i
+ )
for degree in degrees:
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)
+ 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)])
+ [
+ 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)])
+ {
+ (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
@@ -174,7 +223,7 @@ def forms():
c1 = nreps * len(cells) * len(degrees) * len(families) * nelm * 2
assert c == c1
- c0 = len(cells) * len(degrees) * (len(families)-1) * nelm * 2
+ c0 = len(cells) * len(degrees) * (len(families) - 1) * nelm * 2
assert d == c0
assert r == c0
assert h == c0
@@ -193,13 +242,15 @@ 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):
- d = cell.geometric_dimension()
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1), ufl_id=i)
+ d = cell.topological_dimension()
+ domain = Mesh(
+ FiniteElement("Lagrange", cell, 1, (d,), identity_pullback, H1), ufl_id=i
+ )
for k in counts:
V = FiniteElement("Lagrange", cell, 2, (), identity_pullback, H1)
space = FunctionSpace(domain, V)
f = Coefficient(space, count=k)
- g = Coefficient(space, count=k+2)
+ g = Coefficient(space, count=k + 2)
expr = inner(f, g)
renumbering = domain_numbering(*cells)
@@ -232,13 +283,15 @@ def test_terminal_hashdata_does_depend_on_argument_number_values(self):
def forms():
for rep in range(nreps):
for i, cell in enumerate(cells):
- d = cell.geometric_dimension()
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1), ufl_id=i)
+ d = cell.topological_dimension()
+ domain = Mesh(
+ FiniteElement("Lagrange", cell, 1, (d,), identity_pullback, H1), ufl_id=i
+ )
for k in counts:
V = FiniteElement("Lagrange", cell, 2, (), identity_pullback, H1)
space = FunctionSpace(domain, V)
f = Argument(space, k)
- g = Argument(space, k+2)
+ g = Argument(space, k + 2)
expr = inner(f, g)
reprs.add(repr(expr))
@@ -246,7 +299,9 @@ def forms():
yield compute_terminal_hashdata(expr, domain_numbering(*cells))
c, d, r, h = compute_unique_terminal_hashdatas(forms())
- c0 = len(cells) * len(counts) # Number of actually unique cases from a code generation perspective
+ c0 = len(cells) * len(
+ counts
+ ) # Number of actually unique cases from a code generation perspective
c1 = 1 * c0 # Number of unique cases from a symbolic representation perspective
assert len(reprs) == c1
assert len(hashes) == c1
@@ -262,8 +317,8 @@ def test_domain_signature_data_does_not_depend_on_domain_label_value(self):
s1s = set()
s2s = set()
for i, cell in enumerate(cells):
- d = cell.geometric_dimension()
- domain = FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1)
+ d = cell.topological_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)
@@ -285,17 +340,26 @@ def test_terminal_hashdata_does_not_depend_on_domain_label_value(self):
hashes = set()
ufl_ids = [1, 2]
cells = [triangle, quadrilateral]
- 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]
+ domains = [
+ Mesh(
+ FiniteElement(
+ "Lagrange", cell, 1, (cell.topological_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("Lagrange", domain.ufl_cell(), 2, (),
- identity_pullback, H1))
+ V = FunctionSpace(
+ domain,
+ FiniteElement("Lagrange", domain.ufl_cell(), 2, (), identity_pullback, H1),
+ )
f = Coefficient(V, count=0)
v = TestFunction(V)
x = SpatialCoordinate(domain)
@@ -312,8 +376,12 @@ def forms():
yield compute_terminal_hashdata(expr, renumbering)
c, d, r, h = compute_unique_terminal_hashdatas(forms())
- c0 = num_exprs * len(cells) # Number of actually unique cases from a code generation perspective
- c1 = num_exprs * len(domains) # Number of unique cases from a symbolic representation perspective
+ c0 = num_exprs * len(
+ cells
+ ) # Number of actually unique cases from a code generation perspective
+ c1 = num_exprs * len(
+ domains
+ ) # Number of unique cases from a symbolic representation perspective
assert len(reprs) == c1
assert len(hashes) == c1
self.assertEqual(c, nreps * c1) # number of inner loop executions in forms() above
@@ -350,9 +418,9 @@ def hashdatas():
c, d, r, h = compute_unique_multiindex_hashdatas(hashdatas())
assert c == 9
- assert d == 9-1 # (1,0 is repeated, therefore -1)
- assert len(reprs) == 9-1
- assert len(hashes) == 9-1
+ assert d == 9 - 1 # (1,0 is repeated, therefore -1)
+ assert len(reprs) == 9 - 1
+ assert len(hashes) == 9 - 1
def test_multiindex_hashdata_does_not_depend_on_counts(self):
@@ -373,11 +441,12 @@ def hashdatas():
reprs.add(repr(expr))
hashes.add(hash(expr))
yield compute_multiindex_hashdata(expr, {})
+
c, d, r, h = compute_unique_multiindex_hashdatas(hashdatas())
- assert c == 3+9+9
- assert d == 1+1
- assert len(reprs) == 3+9+9
- assert len(hashes) == 3+9+9
+ assert c == 3 + 9 + 9
+ assert d == 1 + 1
+ assert len(reprs) == 3 + 9 + 9
+ assert len(hashes) == 3 + 9 + 9
def test_multiindex_hashdata_depends_on_the_order_indices_are_observed(self):
@@ -387,28 +456,31 @@ def test_multiindex_hashdata_depends_on_the_order_indices_are_observed(self):
def hashdatas():
for rep in range(nrep):
- # Resetting index_numbering for each repetition,
- # resulting in hashdata staying the same for
- # each repetition but repr and hashes changing
- # because new indices are created each repetition.
+ # Resetting index_numbering for each repetition, resulting
+ # in hashdata staying the same for each repetition but repr
+ # and hashes changing because new indices are created each
+ # repetition.
index_numbering = {}
i, j, k, l = indices(4) # noqa: E741
- for expr in (MultiIndex((i,)),
- MultiIndex((i,)), # r
- MultiIndex((i, j)),
- MultiIndex((j, i)),
- MultiIndex((i, j)), # r
- MultiIndex((i, j, k)),
- MultiIndex((k, j, i)),
- MultiIndex((j, i))): # r
+ for expr in (
+ MultiIndex((i,)),
+ MultiIndex((i,)), # r
+ MultiIndex((i, j)),
+ MultiIndex((j, i)),
+ MultiIndex((i, j)), # r
+ MultiIndex((i, j, k)),
+ MultiIndex((k, j, i)),
+ MultiIndex((j, i)),
+ ): # r
reprs.add(repr(expr))
hashes.add(hash(expr))
yield compute_multiindex_hashdata(expr, index_numbering)
+
c, d, r, h = compute_unique_multiindex_hashdatas(hashdatas())
- assert c == nrep*8
+ assert c == nrep * 8
assert d == 5
- assert len(reprs) == nrep*5
- assert len(hashes) == nrep*5
+ assert len(reprs) == nrep * 5
+ assert len(hashes) == nrep * 5
def check_unique_signatures(forms):
@@ -436,70 +508,73 @@ def test_signature_is_affected_by_element_properties(self):
def forms():
for family, sobolev in (("Lagrange", H1), ("Discontinuous Lagrange", L2)):
for cell in (triangle, tetrahedron, quadrilateral):
- d = cell.geometric_dimension()
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1))
+ d = cell.topological_dimension()
+ domain = Mesh(FiniteElement("Lagrange", cell, 1, (d,), identity_pullback, H1))
for degree in (1, 2):
V = FiniteElement(family, cell, degree, (), identity_pullback, sobolev)
space = FunctionSpace(domain, V)
u = Coefficient(space)
v = TestFunction(space)
x = SpatialCoordinate(domain)
- w = as_vector([v]*x.ufl_shape[0])
- f = dot(w, u*x)
- a = f*dx
+ w = as_vector([v] * x.ufl_shape[0])
+ f = dot(w, u * x)
+ a = f * dx
yield a
+
check_unique_signatures(forms())
def test_signature_is_affected_by_domains(self):
def forms():
for cell in (triangle, tetrahedron):
- d = cell.geometric_dimension()
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1))
+ d = cell.topological_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("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)
+ a = u * dx(di) + 2 * u * dx(dj) + 3 * u * ds(dk)
yield a
+
check_unique_signatures(forms())
def test_signature_of_forms_with_diff(self):
def forms():
for i, cell in enumerate([triangle, tetrahedron]):
- d = cell.geometric_dimension()
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1), ufl_id=i)
+ d = cell.topological_dimension()
+ domain = Mesh(FiniteElement("Lagrange", cell, 1, (d,), identity_pullback, H1), ufl_id=i)
for k in (1, 2, 3):
- d = cell.geometric_dimension()
+ d = cell.topological_dimension()
V = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1)
- W = FiniteElement("Lagrange", cell, 1, (d, ), 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)
w = Coefficient(w_space)
vu = variable(u)
vw = variable(w)
- f = vu*dot(vw, vu**k*vw)
+ f = vu * dot(vw, vu**k * vw)
g = diff(f, vu)
h = dot(diff(f, vw), FacetNormal(domain))
- a = f*dx(1) + g*dx(2) + h*ds(0)
+ a = f * dx(1) + g * dx(2) + h * ds(0)
yield a
+
check_unique_signatures(forms())
def test_signature_of_form_depend_on_coefficient_numbering_across_integrals(self):
cell = triangle
V = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", cell, 1, (2,), identity_pullback, H1))
space = FunctionSpace(domain, V)
f = Coefficient(space)
g = Coefficient(space)
- M1 = f*dx(0) + g*dx(1)
- M2 = g*dx(0) + f*dx(1)
- M3 = g*dx(0) + g*dx(1)
+ M1 = f * dx(0) + g * dx(1)
+ M2 = g * dx(0) + f * dx(1)
+ M3 = g * dx(0) + g * dx(1)
self.assertTrue(M1.signature() != M2.signature())
self.assertTrue(M1.signature() != M3.signature())
self.assertTrue(M2.signature() != M3.signature())
@@ -508,21 +583,23 @@ 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):
- d = cell.geometric_dimension()
+ d = cell.topological_dimension()
V = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", cell, 1, (d,), identity_pullback, H1))
space = FunctionSpace(domain, V)
u = Coefficient(space)
v = Coefficient(space)
- fs = [(u*v)+(u/v),
- (u+v)+(u/v),
- (u+v)*(u/v),
- (u*v)*(u*v),
- (u+v)*(u*v), # H1 same
- # (u*v)*(u+v), # H1 same
- (u*v)+(u+v),
- ]
+ fs = [
+ (u * v) + (u / v),
+ (u + v) + (u / v),
+ (u + v) * (u / v),
+ (u * v) * (u * v),
+ (u + v) * (u * v), # H1 same
+ # (u*v)*(u+v), # H1 same
+ (u * v) + (u + v),
+ ]
for f in fs:
- a = f*dx
+ a = f * dx
yield a
+
check_unique_signatures(forms())
diff --git a/test/test_simplify.py b/test/test_simplify.py
index 66a176d9d..1bbef4ba5 100755
--- a/test/test_simplify.py
+++ b/test/test_simplify.py
@@ -1,7 +1,33 @@
import math
-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 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
@@ -11,13 +37,13 @@
def xtest_zero_times_argument(self):
# FIXME: Allow zero forms
element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
- domain = Mesh(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)
- L = 0*v*dx
- a = 0*(u*v)*dx
- b = (0*u)*v*dx
+ L = 0 * v * dx
+ a = 0 * (u * v) * dx
+ b = (0 * u) * v * dx
assert len(compute_form_data(L).arguments) == 1
assert len(compute_form_data(a).arguments) == 2
assert len(compute_form_data(b).arguments) == 2
@@ -25,23 +51,23 @@ def xtest_zero_times_argument(self):
def test_divisions(self):
element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
space = FunctionSpace(domain, element)
f = Coefficient(space)
# Test simplification of division by 1
a = f
- b = f/1
+ b = f / 1
assert a == b
# Test simplification of division by 1.0
a = f
- b = f/1.0
+ b = f / 1.0
assert a == b
# Test simplification of division by of zero by something
- a = 0/f
- b = 0*f
+ a = 0 / f
+ b = 0 * f
assert a == b
# Test simplification of division by self (this simplification has been disabled)
@@ -52,21 +78,21 @@ def test_divisions(self):
def test_products(self):
element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
space = FunctionSpace(domain, element)
f = Coefficient(space)
g = Coefficient(space)
# Test simplification of literal multiplication
- assert f*0 == as_ufl(0)
- assert 0*f == as_ufl(0)
- assert 1*f == f
- assert f*1 == f
- assert as_ufl(2)*as_ufl(3) == as_ufl(6)
- assert as_ufl(2.0)*as_ufl(3.0) == as_ufl(6.0)
+ assert f * 0 == as_ufl(0)
+ assert 0 * f == as_ufl(0)
+ assert 1 * f == f
+ assert f * 1 == f
+ assert as_ufl(2) * as_ufl(3) == as_ufl(6)
+ assert as_ufl(2.0) * as_ufl(3.0) == as_ufl(6.0)
# Test reordering of operands
- assert f*g == g*f
+ assert f * g == g * f
# Test simplification of self-multiplication (this simplification has been disabled)
# assert f*f == f**2
@@ -74,7 +100,7 @@ def test_products(self):
def test_sums(self):
element = FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1)
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
space = FunctionSpace(domain, element)
f = Coefficient(space)
g = Coefficient(space)
@@ -118,13 +144,13 @@ def test_mathfunctions(self):
assert math.exp(a) == exp(a)
assert math.log(a) == ln(a)
# TODO: Implement automatic simplification of conditionals?
- assert a == float(max_value(a, a-1))
+ assert a == float(max_value(a, a - 1))
# TODO: Implement automatic simplification of conditionals?
- assert a == float(min_value(a, a+1))
+ assert a == float(min_value(a, a + 1))
def test_indexing(self):
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
u = VectorConstant(domain)
v = VectorConstant(domain)
@@ -132,7 +158,7 @@ def test_indexing(self):
A2 = as_tensor(A[i, j], (i, j))
assert A2 == A
- Bij = u[i]*v[j]
+ Bij = u[i] * v[j]
Bij2 = as_tensor(Bij, (i, j))[i, j]
as_tensor(Bij, (i, j))
assert Bij2 == Bij
diff --git a/test/test_sobolevspace.py b/test/test_sobolevspace.py
index 28e42a24d..b21760f63 100755
--- a/test/test_sobolevspace.py
+++ b/test/test_sobolevspace.py
@@ -6,8 +6,10 @@
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
+from ufl.sobolevspace import (
+ DirectionalSobolevSpace,
+ SobolevSpace, # noqa: F401
+)
# Construct directional Sobolev spaces, with varying smoothness in
# spatial coordinates
@@ -26,10 +28,10 @@
def test_inclusion():
- assert H2 < H1 # Inclusion
- assert not H2 > H1 # Not included
+ assert H2 < H1 # Inclusion
+ assert not H2 > H1 # Not included
assert HDiv <= HDiv # Reflexivity
- assert H2 < L2 # Transitivity
+ assert H2 < L2 # Transitivity
assert H1 > H2
assert L2 > H1
@@ -84,7 +86,7 @@ def test_contains_h1():
FiniteElement("Lagrange", triangle, 1, (), identity_pullback, H1),
FiniteElement("Lagrange", triangle, 2, (), identity_pullback, H1),
# Some special elements:
- FiniteElement("MTW", triangle, 3, (2, ), contravariant_piola, H1),
+ FiniteElement("MTW", triangle, 3, (2,), contravariant_piola, H1),
FiniteElement("Hermite", triangle, 3, (), "custom", H1),
]
for h1_element in h1_elements:
@@ -115,9 +117,7 @@ def test_contains_h2():
def test_contains_hinf():
- hinf_elements = [
- FiniteElement("Real", triangle, 0, (), identity_pullback, HInf)
- ]
+ hinf_elements = [FiniteElement("Real", triangle, 0, (), identity_pullback, HInf)]
for hinf_element in hinf_elements:
assert hinf_element in HInf
assert hinf_element in H2
@@ -132,9 +132,9 @@ def test_contains_hinf():
def test_contains_hdiv():
hdiv_elements = [
- 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),
+ 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
@@ -149,8 +149,8 @@ def test_contains_hdiv():
def test_contains_hcurl():
hcurl_elements = [
- FiniteElement("N1curl", triangle, 1, (2, ), covariant_piola, HCurl),
- FiniteElement("N2curl", triangle, 1, (2, ), covariant_piola, HCurl),
+ 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
diff --git a/test/test_split.py b/test/test_split.py
index 7a16e4a84..91eb4b0b6 100755
--- a/test/test_split.py
+++ b/test/test_split.py
@@ -9,15 +9,18 @@
def test_split(self):
cell = triangle
- d = cell.geometric_dimension()
- domain = Mesh(FiniteElement("Lagrange", cell, 1, (d, ), identity_pullback, H1))
+ d = cell.topological_dimension()
+ 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)])
+ 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])
@@ -31,15 +34,15 @@ def test_split(self):
# Check that shapes of all these functions are correct:
assert () == Coefficient(f_space).ufl_shape
assert (d,) == Coefficient(v_space).ufl_shape
- assert (d+1,) == Coefficient(w_space).ufl_shape
+ assert (d + 1,) == Coefficient(w_space).ufl_shape
assert (d, d) == Coefficient(t_space).ufl_shape
assert (d, d) == Coefficient(s_space).ufl_shape
# sum of value sizes, not accounting for symmetries:
- assert (3*d*d + 2*d + 2,) == Coefficient(m_space).ufl_shape
+ assert (3 * d * d + 2 * d + 2,) == Coefficient(m_space).ufl_shape
# Shapes of subelements are reproduced:
g = Coefficient(m_space)
- s, = g.ufl_shape
+ (s,) = g.ufl_shape
for g2 in split(g):
s -= product(g2.ufl_shape)
assert s == 0
@@ -51,8 +54,8 @@ def test_split(self):
m2_space = FunctionSpace(domain, m2)
# assert d == 2
# assert (2,2) == Coefficient(v2_space).ufl_shape
- assert (d+d,) == Coefficient(v2_space).ufl_shape
- assert (2*d*d,) == Coefficient(m2_space).ufl_shape
+ assert (d + d,) == Coefficient(v2_space).ufl_shape
+ assert (2 * d * d,) == Coefficient(m2_space).ufl_shape
# Split simple element
t = TestFunction(f_space)
@@ -67,8 +70,7 @@ def test_split(self):
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, 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(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])
assert split(split(t)[1]) == (t[3], as_vector((t[4], t[5])))
assert split(split(split(t)[0])[0]) == (t[0], t[1])
diff --git a/test/test_str.py b/test/test_str.py
index df80d8215..085aee7b2 100755
--- a/test/test_str.py
+++ b/test/test_str.py
@@ -1,6 +1,22 @@
-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 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
@@ -15,11 +31,11 @@ def test_str_float_value(self):
def test_str_zero(self):
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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,))"
- assert str(0*x*x[Index(42)]) == "0 (shape (2,), index labels (42,))"
+ assert str(0 * x) == "0 (shape (2,))"
+ assert str(0 * x * x[Index(42)]) == "0 (shape (2,), index labels (42,))"
def test_str_index(self):
@@ -28,41 +44,45 @@ def test_str_index(self):
def test_str_coordinate(self):
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
assert str(Circumradius(domain)) == "circumradius"
def test_str_diameter(self):
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
assert str(CellDiameter(domain)) == "diameter"
def test_str_facetarea(self):
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
assert str(FacetArea(domain)) == "facetarea"
def test_str_volume(self):
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
assert str(CellVolume(domain)) == "volume"
def test_str_scalar_argument(self):
- 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)))
+ 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"
@@ -75,38 +95,36 @@ def test_str_scalar_argument(self):
def test_str_list_vector():
- domain = Mesh(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1))
+ 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(FiniteElement("Lagrange", tetrahedron, 1, (3, ), identity_pullback, H1))
+ 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(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ 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)))
- a = str(2*x)
- b = str(3*y)
- c = str(4*x)
- d = str(5*y)
+ v = as_matrix(((2 * x, 3 * y), (4 * x, 5 * y)))
+ a = str(2 * x)
+ b = str(3 * y)
+ c = str(4 * x)
+ d = str(5 * y)
assert str(v) == ("[\n [%s, %s],\n [%s, %s]\n]" % (a, b, c, d))
def test_str_list_matrix_with_zero():
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
x, y = SpatialCoordinate(domain)
- v = as_matrix(((2*x, 3*y),
- (0, 0)))
- a = str(2*x)
- b = str(3*y)
+ v = as_matrix(((2 * x, 3 * y), (0, 0)))
+ a = str(2 * x)
+ b = str(3 * y)
c = str(as_vector((0, 0)))
assert str(v) == ("[\n [%s, %s],\n%s\n]" % (a, b, c))
@@ -117,5 +135,8 @@ def test_str_list_matrix_with_zero():
def test_str_element():
elem = FiniteElement("Q", quadrilateral, 1, (), identity_pullback, H1)
- assert repr(elem) == "ufl.finiteelement.FiniteElement(\"Q\", quadrilateral, 1, (), IdentityPullback(), 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 27e75869d..9b5a0fff9 100644
--- a/test/test_strip_forms.py
+++ b/test/test_strip_forms.py
@@ -1,7 +1,18 @@
import gc
import sys
-from ufl import Coefficient, Constant, 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 UFLObject
@@ -51,8 +62,9 @@ def test_strip_form_arguments_strips_data_refs():
assert sys.getrefcount(const_data) == MIN_REF_COUNT
cell = triangle
- domain = AugmentedMesh(FiniteElement("Lagrange", cell, 1, (cell.geometric_dimension(), ),
- identity_pullback, H1), data=mesh_data)
+ domain = AugmentedMesh(
+ FiniteElement("Lagrange", cell, 1, (2,), identity_pullback, H1), data=mesh_data
+ )
element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1)
V = AugmentedFunctionSpace(domain, element, data=fs_data)
@@ -61,7 +73,7 @@ def test_strip_form_arguments_strips_data_refs():
f = AugmentedCoefficient(V, data=coeff_data)
k = AugmentedConstant(V, data=const_data)
- form = k*f*inner(grad(v), grad(u))*dx
+ form = k * f * inner(grad(v), grad(u)) * dx
# Remove extraneous references
del cell, domain, element, V, v, u, f, k
@@ -89,8 +101,9 @@ def test_strip_form_arguments_does_not_change_form():
const_data = object()
cell = triangle
- domain = AugmentedMesh(FiniteElement("Lagrange", cell, 1, (cell.geometric_dimension(), ),
- identity_pullback, H1), data=mesh_data)
+ domain = AugmentedMesh(
+ FiniteElement("Lagrange", cell, 1, (2,), identity_pullback, H1), data=mesh_data
+ )
element = FiniteElement("Lagrange", cell, 1, (), identity_pullback, H1)
V = AugmentedFunctionSpace(domain, element, data=fs_data)
@@ -99,7 +112,7 @@ def test_strip_form_arguments_does_not_change_form():
f = AugmentedCoefficient(V, data=coeff_data)
k = AugmentedConstant(V, data=const_data)
- form = k*f*inner(grad(v), grad(u))*dx
+ form = k * f * inner(grad(v), grad(u)) * dx
stripped_form, mapping = strip_terminal_data(form)
assert stripped_form.signature() == form.signature()
diff --git a/test/test_tensoralgebra.py b/test/test_tensoralgebra.py
index bdb6ad406..4d80e671d 100755
--- a/test/test_tensoralgebra.py
+++ b/test/test_tensoralgebra.py
@@ -2,8 +2,30 @@
import pytest
-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 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
@@ -43,13 +65,13 @@ def test_repeated_as_tensor(self, A, B, u, v):
def test_outer(self, A, B, u, v):
C = outer(u, v)
- D = as_matrix([[10*30, 10*40], [20*30, 20*40]])
+ D = as_matrix([[10 * 30, 10 * 40], [20 * 30, 20 * 40]])
self.assertEqualValues(C, D)
C = outer(A, v)
A, v = A, v
dims = (0, 1)
- D = as_tensor([[[A[i, j]*v[k] for k in dims] for j in dims] for i in dims])
+ D = as_tensor([[[A[i, j] * v[k] for k in dims] for j in dims] for i in dims])
self.assertEqualValues(C, D)
# TODO: Test other ranks
@@ -57,18 +79,18 @@ def test_outer(self, A, B, u, v):
def test_inner(self, A, B, u, v):
C = inner(A, B)
- D = 2*6 + 3*7 + 4*8 + 5*9
+ D = 2 * 6 + 3 * 7 + 4 * 8 + 5 * 9
self.assertEqualValues(C, D)
C = inner(u, v)
- D = 10*30 + 20*40
+ D = 10 * 30 + 20 * 40
self.assertEqualValues(C, D)
def test_pow2_inner(self, A, u):
- domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2, ), identity_pullback, H1))
+ domain = Mesh(FiniteElement("Lagrange", triangle, 1, (2,), identity_pullback, H1))
f = FacetNormal(domain)[0]
- f2 = f*f
+ f2 = f * f
assert f2 == remove_complex_nodes(inner(f, f))
u2 = u**2
@@ -83,13 +105,12 @@ def test_pow2_inner(self, A, u):
def test_dot(self, A, B, u, v):
C = dot(u, v)
- D = 10*30 + 20*40
+ D = 10 * 30 + 20 * 40
self.assertEqualValues(C, D)
C = dot(A, B)
dims = (0, 1)
- D = as_matrix([[sum(A[i, k]*B[k, j] for k in dims)
- for j in dims] for i in dims])
+ D = as_matrix([[sum(A[i, k] * B[k, j] for k in dims) for j in dims] for i in dims])
self.assertEqualValues(C, D)
@@ -131,21 +152,21 @@ def test_perp(self):
def xtest_dev(self, A):
C = dev(A)
- D = 0*C # FIXME: Add expected value here
+ D = 0 * C # FIXME: Add expected value here
self.assertEqualValues(C, D)
def test_skew(self, A):
C = skew(A)
A, dims = A, (0, 1)
- D = 0.5*as_matrix([[A[i, j] - A[j, i] for j in dims] for i in dims])
+ D = 0.5 * as_matrix([[A[i, j] - A[j, i] for j in dims] for i in dims])
self.assertEqualValues(C, D)
def test_sym(self, A):
C = sym(A)
A, dims = A, (0, 1)
- D = 0.5*as_matrix([[A[i, j] + A[j, i] for j in dims] for i in dims])
+ D = 0.5 * as_matrix([[A[i, j] + A[j, i] for j in dims] for i in dims])
self.assertEqualValues(C, D)
@@ -185,7 +206,7 @@ def test_tr(self, A):
def test_det(self, A):
dims = (0, 1)
C = det(A)
- D = sum((-A[i, 0]*A[0, i] if i != 0 else A[i-1, -1]*A[i, 0]) for i in dims)
+ D = sum((-A[i, 0] * A[0, i] if i != 0 else A[i - 1, -1] * A[i, 0]) for i in dims)
self.assertEqualValues(C, D)
@@ -198,6 +219,6 @@ def test_cofac(self, A):
def xtest_inv(self, A):
# FIXME: Test fails probably due to integer division
C = inv(A)
- detA = sum((-A[i, 0]*A[0, i] if i != 0 else A[i-1, -1]*A[i, 0]) for i in (0, 1))
+ detA = sum((-A[i, 0] * A[0, i] if i != 0 else A[i - 1, -1] * A[i, 0]) for i in (0, 1))
D = as_matrix([[(-A[i, j] if i != j else A[i, j]) for j in (-1, 0)] for i in (-1, 0)]) / detA
self.assertEqualValues(C, D)
diff --git a/test/test_utilities.py b/test/test_utilities.py
index 4fdf660d6..a06252898 100755
--- a/test/test_utilities.py
+++ b/test/test_utilities.py
@@ -31,11 +31,14 @@ def test_indexing_to_component():
for i in range(5):
for j in range(3):
for k in range(2):
- assert 15*k+5*j+i == flatten_multiindex((k, j, i), shape_to_strides((2, 3, 5)))
+ assert 15 * k + 5 * j + i == flatten_multiindex(
+ (k, j, i), shape_to_strides((2, 3, 5))
+ )
def test_component_numbering():
from ufl.permutation import build_component_numbering
+
sh = (2, 2)
sm = {(1, 0): (0, 1)}
v, s = build_component_numbering(sh, sm)
@@ -45,8 +48,17 @@ def test_component_numbering():
sh = (3, 3)
sm = {(1, 0): (0, 1), (2, 0): (0, 2), (2, 1): (1, 2)}
v, s = build_component_numbering(sh, sm)
- assert v == {(0, 1): 1, (1, 2): 4, (0, 0): 0, (2, 1): 4, (1, 1): 3,
- (2, 0): 2, (2, 2): 5, (1, 0): 1, (0, 2): 2}
+ assert v == {
+ (0, 1): 1,
+ (1, 2): 4,
+ (0, 0): 0,
+ (2, 1): 4,
+ (1, 1): 3,
+ (2, 0): 2,
+ (2, 2): 5,
+ (1, 0): 1,
+ (0, 2): 2,
+ }
assert s == [(0, 0), (0, 1), (0, 2), (1, 1), (1, 2), (2, 2)]
@@ -143,11 +155,12 @@ def test_index_flattening():
i -= 0
# map back to tensor component:
c2 = unflatten_index(i, shape_to_strides(ts))
- assert (k//2, k % 2) == c2
+ assert (k // 2, k % 2) == c2
def test_stackdict():
from ufl.utils.stacks import StackDict
+
d = StackDict(a=1)
assert d["a"] == 1
d.push("a", 2)
diff --git a/ufl/__init__.py b/ufl/__init__.py
index 59c19e61f..0782ebad7 100644
--- a/ufl/__init__.py
+++ b/ufl/__init__.py
@@ -44,147 +44,149 @@
* Cells::
- - AbstractCell
- - Cell
- - TensorProductCell
- - vertex
- - interval
- - triangle
- - tetrahedron
- - quadrilateral
- - hexahedron
- - prism
- - pyramid
- - pentatope
- - tesseract
+ -AbstractCell
+ -Cell
+ -TensorProductCell
+ -vertex
+ -interval
+ -triangle
+ -tetrahedron
+ -quadrilateral
+ -hexahedron
+ -prism
+ -pyramid
+ -pentatope
+ -tesseract
* Domains::
- - AbstractDomain
- - Mesh
- - MeshView
+ -AbstractDomain
+ -Mesh
+ -MeshView
* Sobolev spaces::
- - L2
- - H1
- - H2
- - HInf
- - HDiv
- - HCurl
- - HEin
- - HDivDiv
+ -L2
+ -H1
+ -H2
+ -HInf
+ -HDiv
+ -HCurl
+ -HEin
+ -HDivDiv
+ -HCurlDiv
* Pull backs::
- - identity_pullback
- - contravariant_piola
- - covariant_piola
- - l2_piola
- - double_contravariant_piola
- - double_covariant_piola
+ -identity_pullback
+ -contravariant_piola
+ -covariant_piola
+ -l2_piola
+ -double_contravariant_piola
+ -double_covariant_piola
+ -covariant_contravariant_piola
* Function spaces::
- - FunctionSpace
- - MixedFunctionSpace
+ -FunctionSpace
+ -MixedFunctionSpace
* Arguments::
- - Argument
- - TestFunction
- - TrialFunction
- - Arguments
- - TestFunctions
- - TrialFunctions
+ -Argument
+ -TestFunction
+ -TrialFunction
+ -Arguments
+ -TestFunctions
+ -TrialFunctions
* Coefficients::
- - Coefficient
- - Constant
- - VectorConstant
- - TensorConstant
+ -Coefficient
+ -Constant
+ -VectorConstant
+ -TensorConstant
* Splitting form arguments in mixed spaces::
- - split
+ -split
* Literal constants::
- - Identity
- - PermutationSymbol
+ -Identity
+ -PermutationSymbol
* Geometric quantities::
- - SpatialCoordinate
- - FacetNormal
- - CellNormal
- - CellVolume
- - CellDiameter
- - Circumradius
- - MinCellEdgeLength
- - MaxCellEdgeLength
- - FacetArea
- - MinFacetEdgeLength
- - MaxFacetEdgeLength
- - Jacobian
- - JacobianDeterminant
- - JacobianInverse
+ -SpatialCoordinate
+ -FacetNormal
+ -CellNormal
+ -CellVolume
+ -CellDiameter
+ -Circumradius
+ -MinCellEdgeLength
+ -MaxCellEdgeLength
+ -FacetArea
+ -MinFacetEdgeLength
+ -MaxFacetEdgeLength
+ -Jacobian
+ -JacobianDeterminant
+ -JacobianInverse
* Indices::
- - Index
- - indices
- - i, j, k, l
- - p, q, r, s
+ -Index
+ -indices
+ -i, j, k, l
+ -p, q, r, s
* Scalar to tensor expression conversion::
- - as_tensor
- - as_vector
- - as_matrix
+ -as_tensor
+ -as_vector
+ -as_matrix
* Unit vectors and matrices::
- - unit_vector
- - unit_vectors
- - unit_matrix
- - unit_matrices
+ -unit_vector
+ -unit_vectors
+ -unit_matrix
+ -unit_matrices
* Tensor algebra operators::
- - outer, inner, dot, cross, perp
- - det, inv, cofac
- - transpose, tr, diag, diag_vector
- - dev, skew, sym
+ -outer, inner, dot, cross, perp
+ -det, inv, cofac
+ -transpose, tr, diag, diag_vector
+ -dev, skew, sym
* Elementwise tensor operators::
- - elem_mult
- - elem_div
- - elem_pow
- - elem_op
+ -elem_mult
+ -elem_div
+ -elem_pow
+ -elem_op
* Differential operators::
- - variable
- - diff,
- - grad, nabla_grad
- - div, nabla_div
- - curl, rot
- - Dx, Dn
+ -variable
+ (-diff,)
+ -grad, nabla_grad
+ -div, nabla_div
+ -curl, rot
+ -Dx, Dn
* Nonlinear functions::
- - max_value, min_value
- - abs, sign
- - sqrt
- - exp, ln, erf
- - cos, sin, tan
- - acos, asin, atan, atan2
- - cosh, sinh, tanh
- - bessel_J, bessel_Y, bessel_I, bessel_K
+ -max_value, min_value
+ -abs, sign
+ -sqrt
+ -exp, ln, erf
+ -cos, sin, tan
+ -acos, asin, atan, atan2
+ -cosh, sinh, tanh
+ -bessel_J, bessel_Y, bessel_I, bessel_K
* Complex operations::
@@ -193,10 +195,10 @@
* Discontinuous Galerkin operators::
- - v('+'), v('-')
- - jump
- - avg
- - cell_avg, facet_avg
+ -v("+"), v("-")
+ -jump
+ -avg
+ -cell_avg, facet_avg
* Conditional operators::
@@ -207,21 +209,21 @@
* Integral measures::
- - dx, ds, dS, dP
- - dc, dC, dO, dI, dX
- - ds_b, ds_t, ds_tb, ds_v, dS_h, dS_v
+ -dx, ds, dS, dP
+ -dc, dC, dO, dI, dX
+ -ds_b, ds_t, ds_tb, ds_v, dS_h, dS_v
* Form transformations::
- - rhs, lhs
- - system
- - functional
- - replace
- - adjoint
- - action
- - energy_norm,
- - sensitivity_rhs
- - derivative
+ -rhs, lhs
+ -system
+ -functional
+ -replace
+ -adjoint
+ -action
+ (-energy_norm,)
+ -sensitivity_rhs
+ -derivative
"""
# Copyright (C) 2008-2016 Martin Sandve Alnæs and Anders Logg
@@ -245,10 +247,17 @@
from math import e, pi
-import ufl.exproperators as __exproperators
from ufl.action import Action
from ufl.adjoint import Adjoint
-from ufl.argument import Argument, Arguments, Coargument, TestFunction, TestFunctions, TrialFunction, TrialFunctions
+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
@@ -259,84 +268,375 @@
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.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.geometry import (
+ CellDiameter,
+ CellNormal,
+ CellVolume,
+ Circumradius,
+ FacetArea,
+ FacetNormal,
+ Jacobian,
+ JacobianDeterminant,
+ JacobianInverse,
+ MaxCellEdgeLength,
+ MaxFacetEdgeLength,
+ MinCellEdgeLength,
+ MinFacetEdgeLength,
+ SpatialCoordinate,
+)
from ufl.integral import Integral
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.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_contravariant_piola,
+ covariant_piola,
+ double_contravariant_piola,
+ double_covariant_piola,
+ identity_pullback,
+ l2_piola,
+)
+from ufl.sobolevspace import H1, H2, L2, HCurl, HCurlDiv, 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.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',
- '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',
- 'AbstractFiniteElement',
- 'FunctionSpace', 'MixedFunctionSpace',
- 'Argument', 'Coargument', 'TestFunction', 'TrialFunction',
- 'Arguments', 'TestFunctions', 'TrialFunctions',
- 'Coefficient', 'Cofunction', 'Coefficients',
- 'Matrix', 'Adjoint', 'Action',
- 'Interpolate', 'interpolate',
- 'ExternalOperator',
- 'Constant', 'VectorConstant', 'TensorConstant',
- 'split',
- 'PermutationSymbol', 'Identity', 'zero', 'as_ufl',
- 'Index', 'indices',
- 'as_tensor', 'as_vector', 'as_matrix',
- 'unit_vector', 'unit_vectors', 'unit_matrix', 'unit_matrices',
- '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',
- 'Form', 'BaseForm', 'FormSum', 'ZeroBaseForm',
- 'Integral', 'Measure', 'register_integral_type', 'integral_types', 'custom_integral_types',
- 'replace', 'derivative', 'action', 'energy_norm', 'rhs', 'lhs', 'extract_blocks',
- 'system', 'functional', 'adjoint', 'sensitivity_rhs',
- 'dx', 'ds', 'dS', 'dP',
- 'dc', 'dC', 'dO', 'dI', 'dX',
- 'ds_b', 'ds_t', 'ds_tb', 'ds_v', 'dS_h', 'dS_v',
- 'vertex', 'interval', 'triangle', 'tetrahedron',
- 'prism', 'pyramid', 'pentatope', 'tesseract',
- 'quadrilateral', 'hexahedron', 'facet',
- 'i', 'j', 'k', 'l', 'p', 'q', 'r', 's',
- 'e', 'pi',
+ "product",
+ "as_cell",
+ "AbstractCell",
+ "Cell",
+ "TensorProductCell",
+ "AbstractDomain",
+ "Mesh",
+ "MeshView",
+ "L2",
+ "H1",
+ "H2",
+ "HCurl",
+ "HDiv",
+ "HInf",
+ "HEin",
+ "HDivDiv",
+ "HCurlDiv",
+ "identity_pullback",
+ "l2_piola",
+ "contravariant_piola",
+ "covariant_piola",
+ "double_contravariant_piola",
+ "double_covariant_piola",
+ "covariant_contravariant_piola",
+ "l2_piola",
+ "MixedPullback",
+ "SymmetricPullback",
+ "AbstractPullback",
+ "SpatialCoordinate",
+ "CellVolume",
+ "CellDiameter",
+ "Circumradius",
+ "MinCellEdgeLength",
+ "MaxCellEdgeLength",
+ "FacetArea",
+ "MinFacetEdgeLength",
+ "MaxFacetEdgeLength",
+ "FacetNormal",
+ "CellNormal",
+ "Jacobian",
+ "JacobianDeterminant",
+ "JacobianInverse",
+ "AbstractFiniteElement",
+ "FunctionSpace",
+ "MixedFunctionSpace",
+ "Argument",
+ "Coargument",
+ "TestFunction",
+ "TrialFunction",
+ "Arguments",
+ "TestFunctions",
+ "TrialFunctions",
+ "Coefficient",
+ "Cofunction",
+ "Coefficients",
+ "Matrix",
+ "Adjoint",
+ "Action",
+ "Interpolate",
+ "interpolate",
+ "ExternalOperator",
+ "Constant",
+ "VectorConstant",
+ "TensorConstant",
+ "split",
+ "PermutationSymbol",
+ "Identity",
+ "zero",
+ "as_ufl",
+ "Index",
+ "indices",
+ "as_tensor",
+ "as_vector",
+ "as_matrix",
+ "unit_vector",
+ "unit_vectors",
+ "unit_matrix",
+ "unit_matrices",
+ "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",
+ "Form",
+ "BaseForm",
+ "FormSum",
+ "ZeroBaseForm",
+ "Integral",
+ "Measure",
+ "register_integral_type",
+ "integral_types",
+ "custom_integral_types",
+ "replace",
+ "derivative",
+ "action",
+ "energy_norm",
+ "rhs",
+ "lhs",
+ "extract_blocks",
+ "system",
+ "functional",
+ "adjoint",
+ "sensitivity_rhs",
+ "dx",
+ "ds",
+ "dS",
+ "dP",
+ "dc",
+ "dC",
+ "dO",
+ "dI",
+ "dX",
+ "ds_b",
+ "ds_t",
+ "ds_tb",
+ "ds_v",
+ "dS_h",
+ "dS_v",
+ "vertex",
+ "interval",
+ "triangle",
+ "tetrahedron",
+ "prism",
+ "pyramid",
+ "pentatope",
+ "tesseract",
+ "quadrilateral",
+ "hexahedron",
+ "facet",
+ "i",
+ "j",
+ "k",
+ "l",
+ "p",
+ "q",
+ "r",
+ "s",
+ "e",
+ "pi",
]
diff --git a/ufl/action.py b/ufl/action.py
index 2401e05ea..fb6416865 100644
--- a/ufl/action.py
+++ b/ufl/action.py
@@ -45,7 +45,8 @@ class Action(BaseForm):
"_arguments",
"_coefficients",
"_domains",
- "_hash")
+ "_hash",
+ )
def __new__(cls, *args, **kw):
"""Create a new Action."""
@@ -71,12 +72,10 @@ def __new__(cls, *args, **kw):
if isinstance(left, (FormSum, Sum)):
# Action distributes over sums on the LHS
- return FormSum(*[(Action(component, right), 1)
- for component in left.ufl_operands])
+ return FormSum(*[(Action(component, right), 1) for component in left.ufl_operands])
if isinstance(right, (FormSum, Sum)):
# Action also distributes over sums on the RHS
- return FormSum(*[(Action(left, component), 1)
- for component in right.ufl_operands])
+ return FormSum(*[(Action(left, component), 1) for component in right.ufl_operands])
return super(Action, cls).__new__(cls)
@@ -98,8 +97,7 @@ def __init__(self, left, right):
def ufl_function_spaces(self):
"""Get the tuple of function spaces of the underlying form."""
if isinstance(self._right, Form):
- return self._left.ufl_function_spaces()[:-1] \
- + self._right.ufl_function_spaces()[1:]
+ return self._left.ufl_function_spaces()[:-1] + self._right.ufl_function_spaces()[1:]
elif isinstance(self._right, Coefficient):
return self._left.ufl_function_spaces()[:-1]
@@ -124,7 +122,15 @@ def _analyze_domains(self):
from ufl.domain import join_domains
# Collect domains
- self._domains = join_domains(chain.from_iterable(e.ufl_domains() for e in self.ufl_operands))
+ self._domains = join_domains(
+ chain.from_iterable(e.ufl_domains() for e in self.ufl_operands)
+ )
+
+ def ufl_domains(self):
+ """Return all domains found in the base form."""
+ if self._domains is None:
+ self._analyze_domains()
+ return self._domains
def equals(self, other):
"""Check if two Actions are equal."""
@@ -158,18 +164,19 @@ def _check_function_spaces(left, right):
# right as a consequence of Leibniz formula.
right, *_ = right.ufl_operands
- # `left` can also be a Coefficient in V (= V**), e.g. `action(Coefficient(V), Cofunction(V.dual()))`.
+ # `left` can also be a Coefficient in V (= V**), e.g.
+ # `action(Coefficient(V), Cofunction(V.dual()))`.
left_arg = left.arguments()[-1] if not isinstance(left, Coefficient) else left
if isinstance(right, (Form, Action, Matrix, ZeroBaseForm)):
if left_arg.ufl_function_space().dual() != right.arguments()[0].ufl_function_space():
raise TypeError("Incompatible function spaces in Action")
elif isinstance(right, (Coefficient, Cofunction, Argument, BaseFormOperator)):
if left_arg.ufl_function_space() != right.ufl_function_space():
-
raise TypeError("Incompatible function spaces in Action")
# `Zero` doesn't contain any information about the function space.
- # -> Not a problem since Action will get simplified with a `ZeroBaseForm`
- # which won't take into account the arguments on the right because of argument contraction.
+ # -> Not a problem since Action will get simplified with a
+ # `ZeroBaseForm` which won't take into account the arguments on
+ # the right because of argument contraction.
# This occurs for:
# `derivative(Action(A, B), u)` with B is an `Expr` such that dB/du == 0
# -> `derivative(B, u)` becomes `Zero` when expanding derivatives since B is an Expr.
@@ -180,7 +187,8 @@ def _check_function_spaces(left, right):
def _get_action_form_arguments(left, right):
"""Perform argument contraction to work out the arguments of Action."""
coefficients = ()
- # `left` can also be a Coefficient in V (= V**), e.g. `action(Coefficient(V), Cofunction(V.dual()))`.
+ # `left` can also be a Coefficient in V (= V**), e.g.
+ # `action(Coefficient(V), Cofunction(V.dual()))`.
left_args = left.arguments()[:-1] if not isinstance(left, Coefficient) else ()
if isinstance(right, BaseForm):
arguments = left_args + right.arguments()[1:]
@@ -189,6 +197,7 @@ def _get_action_form_arguments(left, right):
# Action differentiation pushes differentiation through
# right as a consequence of Leibniz formula.
from ufl.algorithms.analysis import extract_arguments_and_coefficients
+
right_args, right_coeffs = extract_arguments_and_coefficients(right)
arguments = left_args + tuple(right_args)
coefficients += tuple(right_coeffs)
diff --git a/ufl/adjoint.py b/ufl/adjoint.py
index 38f003870..987372a73 100644
--- a/ufl/adjoint.py
+++ b/ufl/adjoint.py
@@ -8,6 +8,8 @@
#
# Modified by Nacime Bouziani, 2021-2022.
+from itertools import chain
+
from ufl.argument import Coargument
from ufl.core.ufl_type import ufl_type
from ufl.form import BaseForm, FormSum, ZeroBaseForm
@@ -32,7 +34,8 @@ class Adjoint(BaseForm):
"_coefficients",
"_domains",
"ufl_operands",
- "_hash")
+ "_hash",
+ )
def __new__(cls, *args, **kw):
"""Create a new Adjoint."""
@@ -46,15 +49,17 @@ def __new__(cls, *args, **kw):
return form._form
elif isinstance(form, FormSum):
# Adjoint distributes over sums
- return FormSum(*[(Adjoint(component), 1)
- for component in form.components()])
+ return FormSum(*[(Adjoint(component), 1) for component in form.components()])
elif isinstance(form, Coargument):
- # The adjoint of a coargument `c: V* -> V*` is the identity matrix mapping from V to V (i.e. V x V* -> R).
- # Equivalently, the adjoint of `c` is its first argument, which is a ufl.Argument defined on the
- # primal space of `c`.
+ # The adjoint of a coargument `c: V* -> V*` is the identity
+ # matrix mapping from V to V (i.e. V x V* -> R).
+ # Equivalently, the adjoint of `c` is its first argument,
+ # which is a ufl.Argument defined on the primal space of
+ # `c`.
primal_arg, _ = form.arguments()
- # Returning the primal argument avoids explicit argument reconstruction, making it
- # a robust strategy for handling subclasses of `ufl.Coargument`.
+ # Returning the primal argument avoids explicit argument
+ # reconstruction, making it a robust strategy for handling
+ # subclasses of `ufl.Coargument`.
return primal_arg
return super(Adjoint, cls).__new__(cls)
@@ -82,7 +87,11 @@ def form(self):
def _analyze_form_arguments(self):
"""The arguments of adjoint are the reverse of the form arguments."""
- self._arguments = self._form.arguments()[::-1]
+ reversed_args = self._form.arguments()[::-1]
+ # Canonical numbering for arguments that is consistent with other BaseForm objects.
+ self._arguments = tuple(
+ type(arg)(arg.ufl_function_space(), number=i) for i, arg in enumerate(reversed_args)
+ )
self._coefficients = self._form.coefficients()
def _analyze_domains(self):
@@ -90,7 +99,15 @@ def _analyze_domains(self):
from ufl.domain import join_domains
# Collect unique domains
- self._domains = join_domains([e.ufl_domain() for e in self.ufl_operands])
+ self._domains = join_domains(
+ chain.from_iterable(e.ufl_domains() for e in self.ufl_operands)
+ )
+
+ def ufl_domains(self):
+ """Return all domains found in the base form."""
+ if self._domains is None:
+ self._analyze_domains()
+ return self._domains
def equals(self, other):
"""Check if two Adjoints are equal."""
@@ -98,8 +115,9 @@ def equals(self, other):
return False
if self is other:
return True
- # Make sure we are returning a boolean as the equality can result in a `ufl.Equation`
- # if the underlying objects are `ufl.BaseForm`.
+ # Make sure we are returning a boolean as the equality can
+ # result in a `ufl.Equation` if the underlying objects are
+ # `ufl.BaseForm`.
return bool(self._form == other._form)
def __str__(self):
diff --git a/ufl/algebra.py b/ufl/algebra.py
index 376c16d02..dea6fd021 100644
--- a/ufl/algebra.py
+++ b/ufl/algebra.py
@@ -19,9 +19,13 @@
# --- Algebraic operators ---
-@ufl_type(num_ops=2,
- inherit_shape_from_operand=0, inherit_indices_from_operand=0,
- binop="__add__", rbinop="__radd__")
+@ufl_type(
+ num_ops=2,
+ inherit_shape_from_operand=0,
+ inherit_indices_from_operand=0,
+ binop="__add__",
+ rbinop="__radd__",
+)
class Sum(Operator):
"""Sum."""
@@ -87,16 +91,14 @@ def __init__(self, a, b):
def evaluate(self, x, mapping, component, index_values):
"""Evaluate."""
- return sum(o.evaluate(x, mapping, component,
- index_values) for o in self.ufl_operands)
+ return sum(o.evaluate(x, mapping, component, index_values) for o in self.ufl_operands)
def __str__(self):
"""Format as a string."""
return " + ".join([parstr(o, self) for o in self.ufl_operands])
-@ufl_type(num_ops=2,
- binop="__mul__", rbinop="__rmul__")
+@ufl_type(num_ops=2, binop="__mul__", rbinop="__rmul__")
class Product(Operator):
"""The product of two or more UFL objects."""
@@ -111,16 +113,20 @@ def __new__(cls, a, b):
# Type checking
# Make sure everything is scalar
if a.ufl_shape or b.ufl_shape:
- raise ValueError("Product can only represent products of scalars, "
- f"got\n {ufl_err_str(a)}\nand\n {ufl_err_str(b)}")
+ raise ValueError(
+ "Product can only represent products of scalars, "
+ f"got\n {ufl_err_str(a)}\nand\n {ufl_err_str(b)}"
+ )
# Simplification
if isinstance(a, Zero) or isinstance(b, Zero):
# Got any zeros? Return zero.
- fi, fid = merge_unique_indices(a.ufl_free_indices,
- a.ufl_index_dimensions,
- b.ufl_free_indices,
- b.ufl_index_dimensions)
+ fi, fid = merge_unique_indices(
+ a.ufl_free_indices,
+ a.ufl_index_dimensions,
+ b.ufl_free_indices,
+ b.ufl_index_dimensions,
+ )
return Zero((), fi, fid)
sa = isinstance(a, ScalarValue)
sb = isinstance(b, ScalarValue)
@@ -154,10 +160,9 @@ def _init(self, a, b):
self.ufl_operands = (a, b)
# Extract indices
- fi, fid = merge_unique_indices(a.ufl_free_indices,
- a.ufl_index_dimensions,
- b.ufl_free_indices,
- b.ufl_index_dimensions)
+ fi, fid = merge_unique_indices(
+ a.ufl_free_indices, a.ufl_index_dimensions, b.ufl_free_indices, b.ufl_index_dimensions
+ )
self.ufl_free_indices = fi
self.ufl_index_dimensions = fid
@@ -173,7 +178,9 @@ def evaluate(self, x, mapping, component, index_values):
sh = self.ufl_shape
if sh:
if sh != ops[-1].ufl_shape:
- raise ValueError("Expecting nonscalar product operand to be the last by convention.")
+ raise ValueError(
+ "Expecting nonscalar product operand to be the last by convention."
+ )
tmp = ops[-1].evaluate(x, mapping, component, index_values)
ops = ops[:-1]
else:
@@ -188,9 +195,7 @@ def __str__(self):
return " * ".join((parstr(a, self), parstr(b, self)))
-@ufl_type(num_ops=2,
- inherit_indices_from_operand=0,
- binop="__div__", rbinop="__rdiv__")
+@ufl_type(num_ops=2, inherit_indices_from_operand=0, binop="__div__", rbinop="__rdiv__")
class Division(Operator):
"""Division."""
@@ -260,9 +265,7 @@ def __str__(self):
return f"{parstr(self.ufl_operands[0], self)} / {parstr(self.ufl_operands[1], self)}"
-@ufl_type(num_ops=2,
- inherit_indices_from_operand=0,
- binop="__pow__", rbinop="__rpow__")
+@ufl_type(num_ops=2, inherit_indices_from_operand=0, binop="__pow__", rbinop="__rpow__")
class Power(Operator):
"""Power."""
@@ -282,7 +285,7 @@ def __new__(cls, a, b):
# Simplification
if isinstance(a, ScalarValue) and isinstance(b, ScalarValue):
- return as_ufl(a._value ** b._value)
+ return as_ufl(a._value**b._value)
if isinstance(b, Zero):
return IntValue(1)
if isinstance(a, Zero) and isinstance(b, ScalarValue):
@@ -324,9 +327,7 @@ def __str__(self):
return f"{parstr(a, self)} ** {parstr(b, self)}"
-@ufl_type(num_ops=1,
- inherit_shape_from_operand=0, inherit_indices_from_operand=0,
- unop="__abs__")
+@ufl_type(num_ops=1, inherit_shape_from_operand=0, inherit_indices_from_operand=0, unop="__abs__")
class Abs(Operator):
"""Absolute value."""
@@ -357,12 +358,11 @@ def evaluate(self, x, mapping, component, index_values):
def __str__(self):
"""Format as a string."""
- a, = self.ufl_operands
+ (a,) = self.ufl_operands
return f"|{parstr(a, self)}|"
-@ufl_type(num_ops=1,
- inherit_shape_from_operand=0, inherit_indices_from_operand=0)
+@ufl_type(num_ops=1, inherit_shape_from_operand=0, inherit_indices_from_operand=0)
class Conj(Operator):
"""Complex conjugate."""
@@ -393,12 +393,11 @@ def evaluate(self, x, mapping, component, index_values):
def __str__(self):
"""Format as a string."""
- a, = self.ufl_operands
+ (a,) = self.ufl_operands
return f"conj({parstr(a, self)})"
-@ufl_type(num_ops=1,
- inherit_shape_from_operand=0, inherit_indices_from_operand=0)
+@ufl_type(num_ops=1, inherit_shape_from_operand=0, inherit_indices_from_operand=0)
class Real(Operator):
"""Real part."""
@@ -431,12 +430,11 @@ def evaluate(self, x, mapping, component, index_values):
def __str__(self):
"""Format as a string."""
- a, = self.ufl_operands
+ (a,) = self.ufl_operands
return f"Re[{parstr(a, self)}]"
-@ufl_type(num_ops=1,
- inherit_shape_from_operand=0, inherit_indices_from_operand=0)
+@ufl_type(num_ops=1, inherit_shape_from_operand=0, inherit_indices_from_operand=0)
class Imag(Operator):
"""Imaginary part."""
@@ -467,5 +465,5 @@ def evaluate(self, x, mapping, component, index_values):
def __str__(self):
"""Format as a string."""
- a, = self.ufl_operands
+ (a,) = self.ufl_operands
return f"Im[{parstr(a, self)}]"
diff --git a/ufl/algorithms/__init__.py b/ufl/algorithms/__init__.py
index 8e3ce5a1a..18dce39fd 100644
--- a/ufl/algorithms/__init__.py
+++ b/ufl/algorithms/__init__.py
@@ -14,7 +14,6 @@
# recommended to use. The __all__ list below is a start based
# on grepping of other FEniCS code for ufl.algorithm imports.
-
__all__ = [
"estimate_total_polynomial_degree",
"sort_elements",
@@ -39,7 +38,6 @@
"replace_terminal_data",
"post_traversal",
"change_to_reference_grad",
- "expand_compounds",
"validate_form",
"FormSplitter",
"extract_arguments",
@@ -57,24 +55,41 @@
]
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.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.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
from ufl.algorithms.formfiles import load_forms, load_ufl_file, read_ufl_file
from ufl.algorithms.formsplitter import FormSplitter
-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.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.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 ba3afe6c6..1d0464a95 100644
--- a/ufl/algorithms/analysis.py
+++ b/ufl/algorithms/analysis.py
@@ -1,4 +1,4 @@
-"""Utility algorithms for inspection of and information extraction from UFL objects in various ways."""
+"""Utility algorithms for inspection of and information extraction from UFL objects."""
# Copyright (C) 2008-2016 Martin Sandve Alnæs
#
@@ -43,6 +43,7 @@ def unique_tuple(objects):
# --- Utilities to extract information from an expression ---
+
def extract_type(a, ufl_types):
"""Build a set of all objects found in a whose class is in ufl_types.
@@ -76,15 +77,22 @@ def extract_type(a, ufl_types):
if all(issubclass(t, Terminal) for t in ufl_types):
# Optimization
- objects = set(o for e in iter_expressions(a)
- for o in traverse_unique_terminals(e)
- if any(isinstance(o, t) for t in ufl_types))
+ objects = set(
+ o
+ for e in iter_expressions(a)
+ for o in traverse_unique_terminals(e)
+ if any(isinstance(o, t) for t in ufl_types)
+ )
else:
- objects = set(o for e in iter_expressions(a)
- for o in unique_pre_traversal(e)
- if any(isinstance(o, t) for t in ufl_types))
-
- # Need to extract objects contained in base form operators whose type is in ufl_types
+ objects = set(
+ o
+ for e in iter_expressions(a)
+ for o in unique_pre_traversal(e)
+ if any(isinstance(o, t) for t in ufl_types)
+ )
+
+ # Need to extract objects contained in base form operators whose
+ # type is in ufl_types
base_form_ops = set(e for e in objects if isinstance(e, BaseFormOperator))
ufl_types_no_args = tuple(t for t in ufl_types if not issubclass(t, BaseArgument))
base_form_objects = ()
@@ -93,16 +101,19 @@ def extract_type(a, ufl_types):
# `N(u; v*) * v * dx` <=> `action(v1 * v * dx, N(...; v*))`
# where `v`, `v1` are `Argument`s and `v*` a `Coargument`.
for ai in tuple(arg for arg in o.argument_slots(isinstance(a, Form))):
- # Extracting BaseArguments of an object of which a Coargument is an argument,
- # then we just return the dual argument of the Coargument and not its primal argument.
+ # Extracting BaseArguments of an object of which a
+ # Coargument is an argument, then we just return the dual
+ # argument of the Coargument and not its primal argument.
if isinstance(ai, Coargument):
new_types = tuple(Coargument if t is BaseArgument else t for t in ufl_types)
base_form_objects += tuple(extract_type(ai, new_types))
else:
base_form_objects += tuple(extract_type(ai, ufl_types))
- # Look for BaseArguments in BaseFormOperator's argument slots only since that's where they are by definition.
- # Don't look into operands, which is convenient for external operator composition, e.g. N1(N2; v*)
- # where N2 is seen as an operator and not a form.
+ # Look for BaseArguments in BaseFormOperator's argument slots
+ # only since that's where they are by definition. Don't look
+ # into operands, which is convenient for external operator
+ # composition, e.g. N1(N2; v*) where N2 is seen as an operator
+ # and not a form.
slots = o.ufl_operands
for ai in slots:
base_form_objects += tuple(extract_type(ai, ufl_types_no_args))
@@ -207,14 +218,16 @@ def extract_arguments_and_coefficients(a):
raise ValueError(
"Found different Arguments with same number and part.\n"
"Did you combine test or trial functions from different spaces?\n"
- "The Arguments found are:\n" + "\n".join(f" {a}" for a in arguments))
+ "The Arguments found are:\n" + "\n".join(f" {a}" for a in arguments)
+ )
# Build count: instance mappings, should be one to one
fcounts = dict((f, f.count()) for f in coefficients)
if len(fcounts) != len(set(fcounts.values())):
raise ValueError(
"Found different coefficients with same counts.\n"
- "The arguments found are:\n" + "\n".join(f" {c}" for c in coefficients))
+ "The arguments found are:\n" + "\n".join(f" {c}" for c in coefficients)
+ )
# Passed checks, so we can safely sort the instances by count
arguments = _sorted_by_number_and_part(arguments)
diff --git a/ufl/algorithms/apply_algebra_lowering.py b/ufl/algorithms/apply_algebra_lowering.py
index e7dc4cd46..ea5b3bbb2 100644
--- a/ufl/algorithms/apply_algebra_lowering.py
+++ b/ufl/algorithms/apply_algebra_lowering.py
@@ -1,4 +1,4 @@
-"""Algorithm for expanding compound expressions into equivalent representations using basic operators."""
+"""Algorithm for expanding compound expressions."""
# Copyright (C) 2008-2016 Martin Sandve Alnæs and Anders Logg
#
@@ -53,8 +53,10 @@ def sym(self, o, A):
def cross(self, o, a, b):
"""Lower a cross."""
+
def c(i, j):
return Product(a[i], b[j]) - Product(a[j], b[i])
+
return as_vector((c(1, 2), c(2, 0), c(0, 1)))
def perp(self, o, a):
@@ -125,12 +127,14 @@ def nabla_grad(self, o, a):
def curl(self, o, a):
"""Lower a curl."""
+
# o = curl a = "[a.dx(1), -a.dx(0)]" if a.ufl_shape == ()
# o = curl a = "cross(nabla, (a0, a1, 0))[2]" if a.ufl_shape == (2,)
# o = curl a = "cross(nabla, a)" if a.ufl_shape == (3,)
def c(i, j):
"""A component of curl."""
return a[j].dx(i) - a[i].dx(j)
+
sh = a.ufl_shape
if sh == ():
return as_vector((a.dx(1), -a.dx(0)))
diff --git a/ufl/algorithms/apply_derivatives.py b/ufl/algorithms/apply_derivatives.py
index 7ef8a0a5f..da7b61da1 100644
--- a/ufl/algorithms/apply_derivatives.py
+++ b/ufl/algorithms/apply_derivatives.py
@@ -1,4 +1,4 @@
-"""This module contains the apply_derivatives algorithm which computes the derivatives of a form of expression."""
+"""Apply derivatives algorithm which computes the derivatives of a form of expression."""
# Copyright (C) 2008-2016 Martin Sandve Alnæs
#
@@ -16,9 +16,33 @@
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,
+ Jacobian,
+ JacobianDeterminant,
+ 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
@@ -26,11 +50,30 @@
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 BaseFormCoordinateDerivative, BaseFormOperatorDerivative, CoordinateDerivative
+from ufl.differentiation import (
+ BaseFormCoordinateDerivative,
+ BaseFormOperatorDerivative,
+ CoordinateDerivative,
+)
from ufl.domain import extract_unique_domain
from ufl.form import Form, ZeroBaseForm
-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.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
@@ -53,8 +96,10 @@ def __init__(self, var_shape):
def expr(self, o):
"""Raise error."""
- raise ValueError(f"Missing differentiation handler for type {o._ufl_class_.__name__}. "
- "Have you added a new type?")
+ raise ValueError(
+ f"Missing differentiation handler for type {o._ufl_class_.__name__}. "
+ "Have you added a new type?"
+ )
def unexpected(self, o):
"""Raise error about unexpected type."""
@@ -62,11 +107,16 @@ def unexpected(self, o):
def override(self, o):
"""Raise error about overriding."""
- raise ValueError(f"Type {o._ufl_class_.__name__} must be overridden in specialized AD rule set.")
+ raise ValueError(
+ f"Type {o._ufl_class_.__name__} must be overridden in specialized AD rule set."
+ )
def derivative(self, o):
"""Raise error."""
- raise ValueError(f"Unhandled derivative type {o._ufl_class_.__name__}, nested differentiation has failed.")
+ raise ValueError(
+ f"Unhandled derivative type {o._ufl_class_.__name__}, "
+ "nested differentiation has failed."
+ )
# --- Some types just don't have any derivative, this is just to
# --- make algorithm structure generic
@@ -74,20 +124,22 @@ def derivative(self, o):
def non_differentiable_terminal(self, o):
"""Return the non-differentiated object.
- Labels and indices are not differentiable: it's convenient to return the non-differentiated object.
+ Labels and indices are not differentiable: it's convenient to
+ return the non-differentiated object.
"""
return o
+
label = non_differentiable_terminal
multi_index = non_differentiable_terminal
# --- Helper functions for creating zeros with the right shapes
def independent_terminal(self, o):
- """Return a zero with the right shape for terminals independent of differentiation variable."""
+ """A zero with correct shape for terminals independent of diff. variable."""
return Zero(o.ufl_shape + self._var_shape)
def independent_operator(self, o):
- """Return a zero with the right shape and indices for operators independent of differentiation variable."""
+ """A zero with correct shape and indices for operators independent of diff. variable."""
return Zero(o.ufl_shape + self._var_shape, o.ufl_free_indices, o.ufl_index_dimensions)
# --- All derivatives need to define grad and averaging
@@ -276,11 +328,13 @@ def power(self, o, fp, gp):
if isinstance(gp, Zero):
# This probably produces better results for the common
# case of f**constant
- op = fp * g * f**(g - 1)
+ op = fp * g * f ** (g - 1)
else:
# Note: This produces expressions like (1/w)*w**5 instead of w**4
# op = o * (fp * g / f + gp * ln(f)) # This reuses o
- op = f**(g - 1) * (g * fp + f * ln(f) * gp) # This gives better accuracy in dolfin integration test
+ op = f ** (g - 1) * (
+ g * fp + f * ln(f) * gp
+ ) # This gives better accuracy in dolfin integration test
# Example: d/dx[x**(x**3)]:
# f = x
@@ -296,7 +350,7 @@ def power(self, o, fp, gp):
def abs(self, o, df):
"""Differentiate an abs."""
- f, = o.ufl_operands
+ (f,) = o.ufl_operands
# return conditional(eq(f, 0), 0, Product(sign(f), df)) abs is
# not complex differentiable, so we workaround the case of a
# real F in complex mode by defensively casting to real inside
@@ -323,8 +377,8 @@ def math_function(self, o, df):
"""Differentiate a math_function."""
# FIXME: Introduce a UserOperator type instead of this hack
# and define user derivative() function properly
- if hasattr(o, 'derivative'):
- f, = o.ufl_operands
+ if hasattr(o, "derivative"):
+ (f,) = o.ufl_operands
return df * o.derivative()
raise ValueError("Unknown math function.")
@@ -338,57 +392,58 @@ def exp(self, o, fp):
def ln(self, o, fp):
"""Differentiate a ln."""
- f, = o.ufl_operands
+ (f,) = o.ufl_operands
if isinstance(f, Zero):
raise ZeroDivisionError()
return fp / f
def cos(self, o, fp):
"""Differentiate a cos."""
- f, = o.ufl_operands
+ (f,) = o.ufl_operands
return fp * -sin(f)
def sin(self, o, fp):
"""Differentiate a sin."""
- f, = o.ufl_operands
+ (f,) = o.ufl_operands
return fp * cos(f)
def tan(self, o, fp):
"""Differentiate a tan."""
- f, = o.ufl_operands
+ (f,) = o.ufl_operands
return 2.0 * fp / (cos(2.0 * f) + 1.0)
def cosh(self, o, fp):
"""Differentiate a cosh."""
- f, = o.ufl_operands
+ (f,) = o.ufl_operands
return fp * sinh(f)
def sinh(self, o, fp):
"""Differentiate a sinh."""
- f, = o.ufl_operands
+ (f,) = o.ufl_operands
return fp * cosh(f)
def tanh(self, o, fp):
"""Differentiate a tanh."""
- f, = o.ufl_operands
+ (f,) = o.ufl_operands
def sech(y):
return (2.0 * cosh(y)) / (cosh(2.0 * y) + 1.0)
- return fp * sech(f)**2
+
+ return fp * sech(f) ** 2
def acos(self, o, fp):
"""Differentiate an acos."""
- f, = o.ufl_operands
+ (f,) = o.ufl_operands
return -fp / sqrt(1.0 - f**2)
def asin(self, o, fp):
"""Differentiate an asin."""
- f, = o.ufl_operands
+ (f,) = o.ufl_operands
return fp / sqrt(1.0 - f**2)
def atan(self, o, fp):
"""Differentiate an atan."""
- f, = o.ufl_operands
+ (f,) = o.ufl_operands
return fp / (1.0 + f**2)
def atan2(self, o, fp, gp):
@@ -398,8 +453,8 @@ def atan2(self, o, fp, gp):
def erf(self, o, fp):
"""Differentiate an erf."""
- f, = o.ufl_operands
- return fp * (2.0 / sqrt(pi) * exp(-f**2))
+ (f,) = o.ufl_operands
+ return fp * (2.0 / sqrt(pi) * exp(-(f**2)))
# --- Bessel functions
@@ -407,7 +462,9 @@ def bessel_j(self, o, nup, fp):
"""Differentiate a bessel_j."""
nu, f = o.ufl_operands
if not (nup is None or isinstance(nup, Zero)):
- raise NotImplementedError("Differentiation of bessel function w.r.t. nu is not supported.")
+ raise NotImplementedError(
+ "Differentiation of bessel function w.r.t. nu is not supported."
+ )
if isinstance(nu, Zero):
op = -bessel_J(1, f)
@@ -419,7 +476,9 @@ def bessel_y(self, o, nup, fp):
"""Differentiate a bessel_y."""
nu, f = o.ufl_operands
if not (nup is None or isinstance(nup, Zero)):
- raise NotImplementedError("Differentiation of bessel function w.r.t. nu is not supported.")
+ raise NotImplementedError(
+ "Differentiation of bessel function w.r.t. nu is not supported."
+ )
if isinstance(nu, Zero):
op = -bessel_Y(1, f)
@@ -431,7 +490,9 @@ def bessel_i(self, o, nup, fp):
"""Differentiate a bessel_i."""
nu, f = o.ufl_operands
if not (nup is None or isinstance(nup, Zero)):
- raise NotImplementedError("Differentiation of bessel function w.r.t. nu is not supported.")
+ raise NotImplementedError(
+ "Differentiation of bessel function w.r.t. nu is not supported."
+ )
if isinstance(nu, Zero):
op = bessel_I(1, f)
@@ -443,7 +504,9 @@ def bessel_k(self, o, nup, fp):
"""Differentiate a bessel_k."""
nu, f = o.ufl_operands
if not (nup is None or isinstance(nup, Zero)):
- raise NotImplementedError("Differentiation of bessel function w.r.t. nu is not supported.")
+ raise NotImplementedError(
+ "Differentiation of bessel function w.r.t. nu is not supported."
+ )
if isinstance(nu, Zero):
op = -bessel_K(1, f)
@@ -568,7 +631,8 @@ def base_form_operator(self, o):
"""Differentiate a base_form_operator."""
# Push the grad through the operator is not legal in most cases:
# -> Not enouth regularity for chain rule to hold!
- # By the time we evaluate `grad(o)`, the operator `o` will have been assembled and substituted by its output.
+ # By the time we evaluate `grad(o)`, the operator `o` will have
+ # been assembled and substituted by its output.
return Grad(o)
def coefficient(self, o):
@@ -609,7 +673,9 @@ def reference_grad(self, o):
# grad(o) == grad(rgrad(rv(f))) -> K_ji*rgrad(rgrad(rv(f)))_rj
f = o.ufl_operands[0]
- valid_operand = f._ufl_is_in_reference_frame_ or isinstance(f, (JacobianInverse, SpatialCoordinate))
+ valid_operand = f._ufl_is_in_reference_frame_ or isinstance(
+ f, (JacobianInverse, SpatialCoordinate, Jacobian, JacobianDeterminant)
+ )
if not valid_operand:
raise ValueError("ReferenceGrad can only wrap a reference frame type!")
domain = extract_unique_domain(f)
@@ -664,10 +730,10 @@ def grad_to_reference_grad(o, K):
class ReferenceGradRuleset(GenericDerivativeRuleset):
"""Apply the reference grad derivative."""
+
def __init__(self, topological_dimension):
"""Initialise."""
- GenericDerivativeRuleset.__init__(self,
- var_shape=(topological_dimension,))
+ GenericDerivativeRuleset.__init__(self, var_shape=(topological_dimension,))
self._Id = Identity(topological_dimension)
# --- Specialized rules for geometric quantities
@@ -722,7 +788,9 @@ def argument(self, o):
def grad(self, o):
"""Differentiate a grad."""
- raise ValueError(f"Grad should have been transformed by this point, but got {type(o).__name__}.")
+ raise ValueError(
+ f"Grad should have been transformed by this point, but got {type(o).__name__}."
+ )
def reference_grad(self, o):
"""Differentiate a reference_grad.
@@ -730,8 +798,7 @@ def reference_grad(self, o):
Represent ref_grad(ref_grad(f)) as RefGrad(RefGrad(f)).
"""
# Check that o is a "differential terminal"
- if not isinstance(o.ufl_operands[0],
- (ReferenceGrad, ReferenceValue, Terminal)):
+ if not isinstance(o.ufl_operands[0], (ReferenceGrad, ReferenceValue, Terminal)):
raise ValueError("Expecting only grads applied to a terminal.")
return ReferenceGrad(o)
@@ -741,6 +808,7 @@ def reference_grad(self, o):
class VariableRuleset(GenericDerivativeRuleset):
"""Differentiate with respect to a variable."""
+
def __init__(self, var):
"""Initialise."""
GenericDerivativeRuleset.__init__(self, var_shape=var.ufl_shape)
@@ -785,9 +853,6 @@ def _make_identity(self, sh):
# Explicitly defining da/dw == 0
argument = GenericDerivativeRuleset.independent_terminal
- # def _argument(self, o):
- # return AnnotatedZero(o.ufl_shape + self._var_shape, arguments=(o,)) # TODO: Missing this type
-
def coefficient(self, o):
"""Differentiate a coefficient.
@@ -838,7 +903,8 @@ def reference_value(self, o):
# convert to reference frame in the first place
raise ValueError(
"Missing implementation: To handle derivatives of rv(f) w.r.t. f for "
- "mapped elements, rewriting to reference frame should not happen first...")
+ "mapped elements, rewriting to reference frame should not happen first..."
+ )
# dv/dv = identity of rank 2*rank(v)
return self._Id
else:
@@ -850,8 +916,7 @@ def reference_grad(self, o):
Variable derivative of a gradient of a terminal must be 0.
"""
- if not isinstance(o.ufl_operands[0],
- (ReferenceGrad, ReferenceValue)):
+ if not isinstance(o.ufl_operands[0], (ReferenceGrad, ReferenceValue)):
raise ValueError("Unexpected argument to reference_grad.")
return self.independent_terminal(o)
@@ -942,7 +1007,10 @@ def coefficient(self, o):
if not isinstance(dos, tuple):
dos = (dos,)
if len(dos) != len(self._v):
- raise ValueError("Got a tuple of arguments, expecting a matching tuple of coefficient derivatives.")
+ raise ValueError(
+ "Got a tuple of arguments, expecting a "
+ "matching tuple of coefficient derivatives."
+ )
dosum = Zero(o.ufl_shape)
for do, v in zip(dos, self._v):
so, oi = as_scalar(do)
@@ -958,7 +1026,9 @@ def coefficient(self, o):
def reference_value(self, o):
"""Differentiate a reference_value."""
- raise NotImplementedError("Currently no support for ReferenceValue in CoefficientDerivative.")
+ raise NotImplementedError(
+ "Currently no support for ReferenceValue in CoefficientDerivative."
+ )
# TODO: This is implementable for regular derivative(M(f),f,v)
# but too messy if customized coefficient derivative
# relations are given by the user. We would only need
@@ -976,7 +1046,9 @@ def reference_value(self, o):
def reference_grad(self, o):
"""Differentiate a reference_grad."""
- raise NotImplementedError("Currently no support for ReferenceGrad in CoefficientDerivative.")
+ raise NotImplementedError(
+ "Currently no support for ReferenceGrad in CoefficientDerivative."
+ )
# TODO: This is implementable for regular derivative(M(f),f,v)
# but too messy if customized coefficient derivative
# relations are given by the user. We would only need
@@ -995,7 +1067,7 @@ def grad(self, g):
ngrads = 0
o = g
while isinstance(o, Grad):
- o, = o.ufl_operands
+ (o,) = o.ufl_operands
ngrads += 1
# `grad(N)` where N is a BaseFormOperator is treated as if `N` was a Coefficient.
if not isinstance(o, (FormArgument, BaseFormOperator)):
@@ -1008,7 +1080,7 @@ def apply_grads(f):
# Find o among all w without any indexing, which makes this
# easy
- for (w, v) in zip(self._w, self._v):
+ for w, v in zip(self._w, self._v):
if o == w and isinstance(v, FormArgument):
# Case: d/dt [w + t v]
return apply_grads(v)
@@ -1048,8 +1120,7 @@ def compute_gprimeterm(ngrads, vval, vcomp, wshape, wcomp):
# Accumulate contributions from variations in different
# components
- for (w, v) in zip(self._w, self._v):
-
+ for w, v in zip(self._w, self._v):
# -- Analyse differentiation variable coefficient -- #
# Can differentiate a Form wrt a BaseFormOperator
@@ -1067,7 +1138,11 @@ def compute_gprimeterm(ngrads, vval, vcomp, wshape, wcomp):
for wcomp, vsub in unwrap_list_tensor(v):
if not isinstance(vsub, Zero):
vval, vcomp = analyse_variation_argument(vsub)
- gprimesum = gprimesum + compute_gprimeterm(ngrads, vval, vcomp, wshape, wcomp)
+ gprimesum = gprimesum + compute_gprimeterm(
+ ngrads, vval, vcomp, wshape, wcomp
+ )
+ elif isinstance(v, Zero):
+ pass
else:
if wshape != ():
@@ -1076,11 +1151,11 @@ def compute_gprimeterm(ngrads, vval, vcomp, wshape, wcomp):
wval, wcomp = w, ()
vval, vcomp = analyse_variation_argument(v)
- gprimesum = gprimesum + compute_gprimeterm(ngrads, vval,
- vcomp, wshape,
- wcomp)
+ gprimesum = gprimesum + compute_gprimeterm(ngrads, vval, vcomp, wshape, wcomp)
- elif isinstance(w, Indexed): # This path is tested in unit tests, but not actually used?
+ elif isinstance(
+ w, Indexed
+ ): # This path is tested in unit tests, but not actually used?
# Case: d/dt [w[...] + t v[...]]
# Case: d/dt [w[...] + t v]
wval, wcomp = w.ufl_operands
@@ -1115,14 +1190,15 @@ def compute_gprimeterm(ngrads, vval, vcomp, wshape, wcomp):
if len(oprimes) != len(self._v):
raise ValueError(
"Got a tuple of arguments, expecting a"
- " matching tuple of coefficient derivatives.")
+ " matching tuple of coefficient derivatives."
+ )
# Compute dg/dw_j = dg/dw_h : v.
# Since we may actually have a tuple of oprimes and vs
# in a 'mixed' space, sum over them all to get the
# complete inner product. Using indices to define a
# non-compound inner product.
- for (oprime, v) in zip(oprimes, self._v):
+ for oprime, v in zip(oprimes, self._v):
raise NotImplementedError("FIXME: Figure out how to do this with ngrads")
so, oi = as_scalar(oprime)
rv = len(v.ufl_shape)
@@ -1144,8 +1220,10 @@ def coordinate_derivative(self, o):
def base_form_operator(self, o, *dfs):
"""Differentiate a base_form_operator.
- If d_coeff = 0 => BaseFormOperator's derivative is taken wrt a variable => we call the appropriate handler.
- Otherwise => differentiation done wrt the BaseFormOperator (dF/dN[Nhat]) => we treat o as a Coefficient.
+ If d_coeff = 0 => BaseFormOperator's derivative is taken wrt a
+ variable => we call the appropriate handler. Otherwise =>
+ differentiation done wrt the BaseFormOperator (dF/dN[Nhat]) =>
+ we treat o as a Coefficient.
"""
d_coeff = self.coefficient(o)
# It also handles the non-scalar case
@@ -1178,7 +1256,8 @@ def coargument(self, o):
def matrix(self, M):
"""Differentiate a matrix."""
# Matrix rule: D_w[v](M) = v if M == w else 0
- # We can't differentiate wrt a matrix so always return zero in the appropriate space
+ # We can't differentiate wrt a matrix so always return zero in
+ # the appropriate space
return ZeroBaseForm(M.arguments() + self._v)
@@ -1191,22 +1270,31 @@ class BaseFormOperatorDerivativeRuleset(GateauxDerivativeRuleset):
def __init__(self, coefficients, arguments, coefficient_derivatives, pending_operations):
"""Initialise."""
- GateauxDerivativeRuleset.__init__(self, coefficients, arguments, coefficient_derivatives, pending_operations)
+ GateauxDerivativeRuleset.__init__(
+ self, coefficients, arguments, coefficient_derivatives, pending_operations
+ )
def pending_operations_recording(base_form_operator_handler):
"""Decorate a function to record pending operations."""
+
def wrapper(self, base_form_op, *dfs):
"""Decorate."""
- # Get the outer `BaseFormOperator` expression, i.e. the operator that is being differentiated.
+ # Get the outer `BaseFormOperator` expression, i.e. the
+ # operator that is being differentiated.
expression = self.pending_operations.expression
- # If the base form operator we observe is different from the outer `BaseFormOperator`:
- # -> Record that `BaseFormOperator` so that `d(expression)/d(base_form_op)` can then be computed later.
+ # If the base form operator we observe is different from the
+ # outer `BaseFormOperator`:
+ # -> Record that `BaseFormOperator` so that
+ # `d(expression)/d(base_form_op)` can then be computed
+ # later.
# Else:
- # -> Compute the Gateaux derivative of `base_form_ops` by calling the appropriate handler.
+ # -> Compute the Gateaux derivative of `base_form_ops` by
+ # calling the appropriate handler.
if expression != base_form_op:
self.pending_operations += (base_form_op,)
return self.coefficient(base_form_op)
return base_form_operator_handler(self, base_form_op, *dfs)
+
return wrapper
@pending_operations_recording
@@ -1232,16 +1320,22 @@ def external_operator(self, N, *dfs):
#
# `\sum_{i} dNdOi(..., Oi, ...; DOi(u)[v], ..., v*)`
#
- # where we differentate wrt u, Oi is the i-th operand, N(..., Oi, ...; ..., v*) an ExternalOperator
- # and v the direction (Argument). dNdOi(..., Oi, ...; DOi(u)[v]) is an ExternalOperator
- # representing the Gateaux-derivative of N. For example:
+ # where we differentate wrt u, Oi is the i-th operand,
+ # N(..., Oi, ...; ..., v*) an ExternalOperator and v the
+ # direction (Argument). dNdOi(..., Oi, ...; DOi(u)[v])
+ # is an ExternalOperator representing the
+ # Gateaux-derivative of N. For example:
# -> From N(u) = u**2, we get `dNdu(u; uhat, v*) = 2 * u * uhat`.
new_args = N.argument_slots() + (df,)
- extop = N._ufl_expr_reconstruct_(*N.ufl_operands, derivatives=derivatives, argument_slots=new_args)
+ extop = N._ufl_expr_reconstruct_(
+ *N.ufl_operands, derivatives=derivatives, argument_slots=new_args
+ )
elif df == 0:
extop = Zero(N.ufl_shape)
else:
- raise NotImplementedError('Frechet derivative of external operators need to be provided!')
+ raise NotImplementedError(
+ "Frechet derivative of external operators need to be provided!"
+ )
result += (extop,)
return sum(result)
@@ -1274,49 +1368,47 @@ def grad(self, o, f):
"""Apply to a grad."""
rules = GradRuleset(o.ufl_shape[-1])
key = (GradRuleset, o.ufl_shape[-1])
- return map_expr_dag(rules, f,
- vcache=self.vcaches[key],
- rcache=self.rcaches[key])
+ return map_expr_dag(rules, f, vcache=self.vcaches[key], rcache=self.rcaches[key])
def reference_grad(self, o, f):
"""Apply to a reference_grad."""
rules = ReferenceGradRuleset(o.ufl_shape[-1]) # FIXME: Look over this and test better.
key = (ReferenceGradRuleset, o.ufl_shape[-1])
- return map_expr_dag(rules, f,
- vcache=self.vcaches[key],
- rcache=self.rcaches[key])
+ return map_expr_dag(rules, f, vcache=self.vcaches[key], rcache=self.rcaches[key])
def variable_derivative(self, o, f, dummy_v):
"""Apply to a variable_derivative."""
op = o.ufl_operands[1]
rules = VariableRuleset(op)
key = (VariableRuleset, op)
- return map_expr_dag(rules, f,
- vcache=self.vcaches[key],
- rcache=self.rcaches[key])
+ return map_expr_dag(rules, f, vcache=self.vcaches[key], rcache=self.rcaches[key])
def coefficient_derivative(self, o, f, dummy_w, dummy_v, dummy_cd):
"""Apply to a coefficient_derivative."""
dummy, w, v, cd = o.ufl_operands
- pending_operations = BaseFormOperatorDerivativeRecorder(f, w, arguments=v, coefficient_derivatives=cd)
+ pending_operations = BaseFormOperatorDerivativeRecorder(
+ f, w, arguments=v, coefficient_derivatives=cd
+ )
rules = GateauxDerivativeRuleset(w, v, cd, pending_operations)
key = (GateauxDerivativeRuleset, w, v, cd)
- # We need to go through the dag first to record the pending operations
- mapped_expr = map_expr_dag(rules, f,
- vcache=self.vcaches[key],
- rcache=self.rcaches[key])
- # Need to account for pending operations that have been stored in other integrands
+ # We need to go through the dag first to record the pending
+ # operations
+ mapped_expr = map_expr_dag(rules, f, vcache=self.vcaches[key], rcache=self.rcaches[key])
+ # Need to account for pending operations that have been stored
+ # in other integrands
self.pending_operations += pending_operations
return mapped_expr
def base_form_operator_derivative(self, o, f, dummy_w, dummy_v, dummy_cd):
"""Apply to a base_form_operator_derivative."""
dummy, w, v, cd = o.ufl_operands
- pending_operations = BaseFormOperatorDerivativeRecorder(f, w, arguments=v, coefficient_derivatives=cd)
+ pending_operations = BaseFormOperatorDerivativeRecorder(
+ f, w, arguments=v, coefficient_derivatives=cd
+ )
rules = BaseFormOperatorDerivativeRuleset(w, v, cd, pending_operations=pending_operations)
key = (BaseFormOperatorDerivativeRuleset, w, v, cd)
if isinstance(f, ZeroBaseForm):
- arg, = v.ufl_operands
+ (arg,) = v.ufl_operands
arguments = f.arguments()
# derivative(F, u, du) with `du` a Coefficient
# is equivalent to taking the action of the derivative.
@@ -1325,9 +1417,7 @@ def base_form_operator_derivative(self, o, f, dummy_w, dummy_v, dummy_cd):
arguments += (arg,)
return ZeroBaseForm(arguments)
# We need to go through the dag first to record the pending operations
- mapped_expr = map_expr_dag(rules, f,
- vcache=self.vcaches[key],
- rcache=self.rcaches[key])
+ mapped_expr = map_expr_dag(rules, f, vcache=self.vcaches[key], rcache=self.rcaches[key])
mapped_f = rules.coefficient(f)
if mapped_f != 0:
@@ -1342,19 +1432,23 @@ def coordinate_derivative(self, o, f, dummy_w, dummy_v, dummy_cd):
"""Apply to a coordinate_derivative."""
o_ = o.ufl_operands
key = (CoordinateDerivative, o_[0])
- return CoordinateDerivative(map_expr_dag(self, o_[0],
- vcache=self.vcaches[key],
- rcache=self.rcaches[key]),
- o_[1], o_[2], o_[3])
+ return CoordinateDerivative(
+ map_expr_dag(self, o_[0], vcache=self.vcaches[key], rcache=self.rcaches[key]),
+ o_[1],
+ o_[2],
+ o_[3],
+ )
def base_form_coordinate_derivative(self, o, f, dummy_w, dummy_v, dummy_cd):
"""Apply to a base_form_coordinate_derivative."""
o_ = o.ufl_operands
key = (BaseFormCoordinateDerivative, o_[0])
- return BaseFormCoordinateDerivative(map_expr_dag(self, o_[0],
- vcache=self.vcaches[key],
- rcache=self.rcaches[key]),
- o_[1], o_[2], o_[3])
+ return BaseFormCoordinateDerivative(
+ map_expr_dag(self, o_[0], vcache=self.vcaches[key], rcache=self.rcaches[key]),
+ o_[1],
+ o_[2],
+ o_[3],
+ )
def indexed(self, o, Ap, ii): # TODO: (Partially) duplicated in generic rules
"""Apply to an indexed."""
@@ -1389,15 +1483,18 @@ def indexed(self, o, Ap, ii): # TODO: (Partially) duplicated in generic rules
return op
-class BaseFormOperatorDerivativeRecorder():
+class BaseFormOperatorDerivativeRecorder:
"""A derivative recorded for a base form operator."""
def __init__(self, expression, var, **kwargs):
"""Initialise."""
base_form_ops = kwargs.pop("base_form_ops", ())
- if kwargs.keys() != {'arguments', 'coefficient_derivatives'}:
- raise ValueError("Only `arguments` and `coefficient_derivatives` are allowed as derivative arguments.")
+ if kwargs.keys() != {"arguments", "coefficient_derivatives"}:
+ raise ValueError(
+ "Only `arguments` and `coefficient_derivatives` are "
+ "allowed as derivative arguments."
+ )
self.expression = expression
self.var = var
@@ -1418,18 +1515,23 @@ def __add__(self, other):
base_form_ops = self.base_form_ops + other
elif isinstance(other, BaseFormOperatorDerivativeRecorder):
if self.der_kwargs != other.der_kwargs:
- raise ValueError(f"Derivative arguments must match when summing {type(self).__name__} objects.")
+ raise ValueError(
+ f"Derivative arguments must match when summing {type(self).__name__} objects."
+ )
base_form_ops = self.base_form_ops + other.base_form_ops
else:
- raise NotImplementedError(f"Sum of {type(self)} and {type(other)} objects is not supported.")
+ raise NotImplementedError(
+ f"Sum of {type(self)} and {type(other)} objects is not supported."
+ )
- return BaseFormOperatorDerivativeRecorder(self.expression, self.var,
- base_form_ops=base_form_ops,
- **self.der_kwargs)
+ return BaseFormOperatorDerivativeRecorder(
+ self.expression, self.var, base_form_ops=base_form_ops, **self.der_kwargs
+ )
def __radd__(self, other):
"""Add."""
- # Recording order doesn't matter as collected `BaseFormOperator`s are sorted later on.
+ # Recording order doesn't matter as collected
+ # `BaseFormOperator`s are sorted later on.
return self.__add__(other)
def __iadd__(self, other):
@@ -1457,8 +1559,10 @@ def apply_derivatives(expression):
rules = DerivativeRuleDispatcher()
# If we hit a base form operator (bfo), then if `var` is:
- # - a BaseFormOperator → Return `d(expression)/dw` where `w` is the coefficient produced by the bfo `var`.
- # - else → Record the bfo on the MultiFunction object and returns 0.
+ # - a BaseFormOperator → Return `d(expression)/dw` where `w` is
+ # the coefficient produced by the bfo `var`.
+ # - else → Record the bfo on the MultiFunction object and returns
+ # - 0.
# Example:
# → If derivative(F(u, N(u); v), u) was taken the following line would compute `∂F/∂u`.
dexpression_dvar = map_integrand_dags(rules, expression)
@@ -1474,29 +1578,40 @@ def apply_derivatives(expression):
else:
dexpression_dvar = ()
- # Retrieve the base form operators, var, and the argument and coefficient_derivatives for `derivative`
+ # Retrieve the base form operators, var, and the argument and
+ # coefficient_derivatives for `derivative`
var = pending_operations.var
base_form_ops = pending_operations.base_form_ops
der_kwargs = pending_operations.der_kwargs
for N in sorted(set(base_form_ops), key=lambda x: x.count()):
# -- Replace dexpr/dvar by dexpr/dN -- #
- # We don't use `apply_derivatives` since the differentiation is done via `\partial` and not `d`.
- dexpr_dN = map_integrand_dags(rules, replace_derivative_nodes(expression, {var.ufl_operands[0]: N}))
+ # We don't use `apply_derivatives` since the differentiation is
+ # done via `\partial` and not `d`.
+ dexpr_dN = map_integrand_dags(
+ rules, replace_derivative_nodes(expression, {var.ufl_operands[0]: N})
+ )
# -- Add the BaseFormOperatorDerivative node -- #
- var_arg, = der_kwargs['arguments'].ufl_operands
- cd = der_kwargs['coefficient_derivatives']
- # Not always the case since `derivative`'s syntax enables one to use a Coefficient as the Gateaux direction
+ (var_arg,) = der_kwargs["arguments"].ufl_operands
+ cd = der_kwargs["coefficient_derivatives"]
+ # Not always the case since `derivative`'s syntax enables one to
+ # use a Coefficient as the Gateaux direction
if isinstance(var_arg, BaseArgument):
- # Construct the argument number based on the BaseFormOperator arguments instead of naively
- # using `var_arg`. This is critical when BaseFormOperators are used inside 0-forms.
+ # Construct the argument number based on the
+ # BaseFormOperator arguments instead of naively using
+ # `var_arg`. This is critical when BaseFormOperators are
+ # used inside 0-forms.
#
# Example: F = 0.5 * u** 2 * dx + 0.5 * N(u; v*)** 2 * dx
# -> dFdu[vhat] = + Action(, dNdu(u; v1, v*))
- # with `vhat` a 0-numbered argument, and where `v1` and `vhat` have the same function space but
- # a different number. Here, applying `vhat` (`var_arg`) naively would result in `dNdu(u; vhat, v*)`,
- # i.e. the 2-forms `dNdu` would have two 0-numbered arguments. Instead we increment the argument number
- # of `vhat` to form `v1`.
- var_arg = type(var_arg)(var_arg.ufl_function_space(), number=len(N.arguments()), part=var_arg.part())
+ # with `vhat` a 0-numbered argument, and where `v1` and
+ # `vhat` have the same function space but a different
+ # number. Here, applying `vhat` (`var_arg`) naively would
+ # result in `dNdu(u; vhat, v*)`, i.e. the 2-forms `dNdu`
+ # would have two 0-numbered arguments. Instead we increment
+ # the argument number of `vhat` to form `v1`.
+ var_arg = type(var_arg)(
+ var_arg.ufl_function_space(), number=len(N.arguments()), part=var_arg.part()
+ )
dN_dvar = apply_derivatives(BaseFormOperatorDerivative(N, var, ExprList(var_arg), cd))
# -- Sum the Action: dF/du = ∂F/∂u + \sum_{i=1,...} Action(∂F/∂Ni, dNi/du) -- #
if not (isinstance(dexpr_dN, Form) and len(dexpr_dN.integrals()) == 0):
@@ -1545,7 +1660,9 @@ def __init__(self, coefficients, arguments, coefficient_derivatives):
def coefficient(self, o):
"""Differentiate a coefficient."""
- raise NotImplementedError("CoordinateDerivative of coefficient in physical space is not implemented.")
+ raise NotImplementedError(
+ "CoordinateDerivative of coefficient in physical space is not implemented."
+ )
def grad(self, o):
"""Differentiate a grad."""
@@ -1558,8 +1675,10 @@ def spatial_coordinate(self, o):
if do is not None:
return do
else:
- raise NotImplementedError("CoordinateDerivative found a SpatialCoordinate that is different "
- "from the one being differentiated.")
+ raise NotImplementedError(
+ "CoordinateDerivative found a SpatialCoordinate that is different "
+ "from the one being differentiated."
+ )
def reference_value(self, o):
"""Differentiate a reference_value."""
@@ -1575,7 +1694,7 @@ def reference_grad(self, g):
o = g
ngrads = 0
while isinstance(o, ReferenceGrad):
- o, = o.ufl_operands
+ (o,) = o.ufl_operands
ngrads += 1
if not (isinstance(o, SpatialCoordinate) or isinstance(o.ufl_operands[0], FormArgument)):
raise ValueError(f"Expecting gradient of a FormArgument, not {ufl_err_str(o)}")
@@ -1587,8 +1706,12 @@ def apply_grads(f):
# Find o among all w without any indexing, which makes this
# easy
- for (w, v) in zip(self._w, self._v):
- if o == w and isinstance(v, ReferenceValue) and isinstance(v.ufl_operands[0], FormArgument):
+ for w, v in zip(self._w, self._v):
+ if (
+ o == w
+ and isinstance(v, ReferenceValue)
+ and isinstance(v.ufl_operands[0], FormArgument)
+ ):
# Case: d/dt [w + t v]
return apply_grads(v)
return self.independent_terminal(o)
@@ -1596,8 +1719,10 @@ def apply_grads(f):
def jacobian(self, o):
"""Differentiate a jacobian."""
# d (grad_X(x))/d x => grad_X(Argument(x.function_space())
- for (w, v) in zip(self._w, self._v):
- if extract_unique_domain(o) == extract_unique_domain(w) and isinstance(v.ufl_operands[0], FormArgument):
+ for w, v in zip(self._w, self._v):
+ if extract_unique_domain(o) == extract_unique_domain(w) and isinstance(
+ v.ufl_operands[0], FormArgument
+ ):
return ReferenceGrad(v)
return self.independent_terminal(o)
@@ -1636,10 +1761,12 @@ def coefficient_derivative(self, o):
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 isinstance(space.pullback, CustomPullback):
raise NotImplementedError(
- "CoordinateDerivative is not supported for elements with custom pull back.")
+ "CoordinateDerivative is not supported for elements with custom pull back."
+ )
_, w, v, cd = o.ufl_operands
rules = CoordinateDerivativeRuleset(w, v, cd)
diff --git a/ufl/algorithms/apply_function_pullbacks.py b/ufl/algorithms/apply_function_pullbacks.py
index a8c1b8acf..124c2a2dd 100644
--- a/ufl/algorithms/apply_function_pullbacks.py
+++ b/ufl/algorithms/apply_function_pullbacks.py
@@ -1,4 +1,4 @@
-"""Algorithm for replacing gradients in an expression with reference gradients and coordinate mappings."""
+"""Algorithm for replacing gradients in an expression."""
# Copyright (C) 2008-2016 Martin Sandve Alnæs
#
@@ -30,16 +30,20 @@ def form_argument(self, o):
# Represent 0-derivatives of form arguments on reference
# element
r = ReferenceValue(o)
+ space = o.ufl_function_space()
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}'")
+ "Expecting reference space expression with shape "
+ f"'{element.reference_value_shape}', 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}'")
+ if f.ufl_shape != space.value_shape:
+ raise ValueError(
+ f"Expecting pulled back expression with shape '{space.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 c1a39d863..1dbc8e1c0 100644
--- a/ufl/algorithms/apply_geometry_lowering.py
+++ b/ufl/algorithms/apply_geometry_lowering.py
@@ -14,10 +14,31 @@
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
@@ -30,6 +51,7 @@
class GeometryLoweringApplier(MultiFunction):
"""Geometry lowering."""
+
def __init__(self, preserve_types=()):
"""Initialise."""
MultiFunction.__init__(self)
@@ -180,8 +202,10 @@ def facet_cell_coordinate(self, o):
if self._preserve_types[o._ufl_typecode_]:
return o
- raise ValueError("Missing computation of facet reference coordinates "
- "from physical coordinates via mappings.")
+ raise ValueError(
+ "Missing computation of facet reference coordinates "
+ "from physical coordinates via mappings."
+ )
@memoized_handler
def cell_volume(self, o):
@@ -256,7 +280,7 @@ def circumradius(self, o):
lb = elen[4] * elen[1]
lc = elen[5] * elen[0]
# p = perimeter
- p = (la + lb + lc)
+ p = la + lb + lc
# s = semiperimeter
s = p / 2
# area of intermediate triangle with Herons formula
@@ -280,7 +304,7 @@ def _reduce_cell_edge_length(self, o, reduction_op):
domain = extract_unique_domain(o)
- if domain.ufl_coordinate_element().embedded_superdegree > 1:
+ if domain.ufl_coordinate_element().embedded_subdegree > 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
@@ -305,7 +329,7 @@ def cell_diameter(self, o):
domain = extract_unique_domain(o)
- if domain.ufl_coordinate_element().embedded_superdegree > 1:
+ if domain.ufl_coordinate_element().embedded_subdegree > 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
@@ -342,7 +366,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 domain.ufl_coordinate_element().embedded_superdegree > 1:
+ elif domain.ufl_coordinate_element().embedded_subdegree > 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
@@ -431,7 +455,9 @@ def facet_normal(self, o):
r = n
if r.ufl_shape != o.ufl_shape:
- raise ValueError(f"Inconsistent dimensions (in={o.ufl_shape[0]}, out={r.ufl_shape[0]}).")
+ raise ValueError(
+ f"Inconsistent dimensions (in={o.ufl_shape[0]}, out={r.ufl_shape[0]})."
+ )
return r
@@ -445,8 +471,9 @@ def apply_geometry_lowering(form, preserve_types=()):
preserve_types: Preserved types
"""
if isinstance(form, Form):
- newintegrals = [apply_geometry_lowering(integral, preserve_types)
- for integral in form.integrals()]
+ newintegrals = [
+ apply_geometry_lowering(integral, preserve_types) for integral in form.integrals()
+ ]
return Form(newintegrals)
elif isinstance(form, Integral):
diff --git a/ufl/algorithms/apply_integral_scaling.py b/ufl/algorithms/apply_integral_scaling.py
index 7e5e35ff2..81c6252a1 100644
--- a/ufl/algorithms/apply_integral_scaling.py
+++ b/ufl/algorithms/apply_integral_scaling.py
@@ -1,4 +1,4 @@
-"""Algorithm for replacing gradients in an expression with reference gradients and coordinate mappings."""
+"""Algorithm for replacing gradients in an expression."""
# Copyright (C) 2013-2016 Martin Sandve Alnæs
#
@@ -8,7 +8,13 @@
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.classes import (
+ FacetJacobianDeterminant,
+ Form,
+ Integral,
+ JacobianDeterminant,
+ QuadratureWeight,
+)
from ufl.differentiation import CoordinateDerivative
from ufl.measure import custom_integral_types, point_integral_types
@@ -52,7 +58,7 @@ def compute_integrand_scaling_factor(integral):
# side and quadrature weight
detFJ = FacetJacobianDeterminant(domain)
degree = estimate_total_polynomial_degree(apply_geometry_lowering(detFJ))
- scale = detFJ('+') * weight
+ scale = detFJ("+") * weight
else:
# No need to scale 'integral' over a vertex
scale = 1
@@ -77,8 +83,7 @@ def apply_integral_scaling(form):
# TODO: Consider adding an in_reference_frame property to Integral
# and checking it here and setting it in the returned form
if isinstance(form, Form):
- newintegrals = [apply_integral_scaling(integral)
- for integral in form.integrals()]
+ newintegrals = [apply_integral_scaling(integral) for integral in form.integrals()]
return Form(newintegrals)
elif isinstance(form, Integral):
@@ -107,9 +112,12 @@ def scale_coordinate_derivative(o, scale):
"""Scale the coordinate derivative."""
o_ = o.ufl_operands
if isinstance(o, CoordinateDerivative):
- return CoordinateDerivative(scale_coordinate_derivative(o_[0], scale), o_[1], o_[2], o_[3])
+ return CoordinateDerivative(
+ scale_coordinate_derivative(o_[0], scale), o_[1], o_[2], o_[3]
+ )
else:
return scale * o
+
newintegrand = scale_coordinate_derivative(integrand, scale)
return integral.reconstruct(integrand=newintegrand, metadata=md)
diff --git a/ufl/algorithms/apply_restrictions.py b/ufl/algorithms/apply_restrictions.py
index 8f788a009..a7550d722 100644
--- a/ufl/algorithms/apply_restrictions.py
+++ b/ufl/algorithms/apply_restrictions.py
@@ -1,7 +1,7 @@
"""Apply restrictions.
-This module contains the apply_restrictions algorithm which propagates restrictions in a form
-towards the terminals.
+This module contains the apply_restrictions algorithm which propagates
+restrictions in a form towards the terminals.
"""
# Copyright (C) 2008-2016 Martin Sandve Alnæs
@@ -10,7 +10,6 @@
#
# SPDX-License-Identifier: LGPL-3.0-or-later
-
from ufl.algorithms.map_integrands import map_integrand_dags
from ufl.classes import Restricted
from ufl.corealg.map_dag import map_expr_dag
@@ -32,8 +31,7 @@ def __init__(self, side=None):
self.vcaches = {"+": {}, "-": {}}
self.rcaches = {"+": {}, "-": {}}
if self.current_restriction is None:
- self._rp = {"+": RestrictionPropagator("+"),
- "-": RestrictionPropagator("-")}
+ self._rp = {"+": RestrictionPropagator("+"), "-": RestrictionPropagator("-")}
def restricted(self, o):
"""When hitting a restricted quantity, visit child with a separate restriction algorithm."""
@@ -43,14 +41,18 @@ def restricted(self, o):
raise ValueError("Cannot restrict an expression twice.")
# Configure a propagator for this side and apply to subtree
side = o.side()
- return map_expr_dag(self._rp[side], o.ufl_operands[0],
- vcache=self.vcaches[side],
- rcache=self.rcaches[side])
+ return map_expr_dag(
+ self._rp[side], o.ufl_operands[0], vcache=self.vcaches[side], rcache=self.rcaches[side]
+ )
# --- Reusable rules
def _ignore_restriction(self, o):
- """Ignore current restriction, quantity is independent of side also from a computational point of view."""
+ """Ignore current restriction.
+
+ Quantity is independent of side also from a computational point
+ of view.
+ """
return o
def _require_restriction(self, o):
@@ -99,7 +101,7 @@ def variable(self, o, op, label):
def reference_value(self, o):
"""Reference value of something follows same restriction rule as the underlying object."""
- f, = o.ufl_operands
+ (f,) = o.ufl_operands
assert f._ufl_is_terminal_
g = self(f)
if isinstance(g, Restricted):
@@ -140,8 +142,8 @@ def reference_value(self, o):
def coefficient(self, o):
"""Restrict a coefficient.
- Allow coefficients to be unrestricted (apply default if so) if the values are fully continuous
- across the facet.
+ Allow coefficients to be unrestricted (apply default if so) if
+ the values are fully continuous across the facet.
"""
if o.ufl_element() in H1:
# If the coefficient _value_ is _fully_ continuous
@@ -174,11 +176,11 @@ def facet_normal(self, o):
def apply_restrictions(expression):
"""Propagate restriction nodes to wrap differential terminals directly."""
- integral_types = [k for k in integral_type_to_measure_name.keys()
- if k.startswith("interior_facet")]
+ integral_types = [
+ k for k in integral_type_to_measure_name.keys() if k.startswith("interior_facet")
+ ]
rules = RestrictionPropagator()
- return map_integrand_dags(rules, expression,
- only_integral_type=integral_types)
+ return map_integrand_dags(rules, expression, only_integral_type=integral_types)
class DefaultRestrictionApplier(MultiFunction):
@@ -190,8 +192,7 @@ def __init__(self, side=None):
self.current_restriction = side
self.default_restriction = "+"
if self.current_restriction is None:
- self._rp = {"+": DefaultRestrictionApplier("+"),
- "-": DefaultRestrictionApplier("-")}
+ self._rp = {"+": DefaultRestrictionApplier("+"), "-": DefaultRestrictionApplier("-")}
def terminal(self, o):
"""Apply to terminal."""
@@ -241,8 +242,8 @@ def apply_default_restrictions(expression):
This applies a default restriction to such terminals if unrestricted.
"""
- integral_types = [k for k in integral_type_to_measure_name.keys()
- if k.startswith("interior_facet")]
+ integral_types = [
+ k for k in integral_type_to_measure_name.keys() if k.startswith("interior_facet")
+ ]
rules = DefaultRestrictionApplier()
- return map_integrand_dags(rules, expression,
- only_integral_type=integral_types)
+ return map_integrand_dags(rules, expression, only_integral_type=integral_types)
diff --git a/ufl/algorithms/balancing.py b/ufl/algorithms/balancing.py
index 477ec3f6f..e671d01d0 100644
--- a/ufl/algorithms/balancing.py
+++ b/ufl/algorithms/balancing.py
@@ -6,20 +6,31 @@
#
# 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
modifier_precedence = [
- ReferenceValue, ReferenceGrad, Grad, CellAvg, FacetAvg, PositiveRestricted,
- NegativeRestricted, Indexed
+ ReferenceValue,
+ ReferenceGrad,
+ Grad,
+ CellAvg,
+ FacetAvg,
+ PositiveRestricted,
+ NegativeRestricted,
+ Indexed,
]
-modifier_precedence = {
- m._ufl_handler_name_: i
- for i, m in enumerate(modifier_precedence)
-}
+modifier_precedence = {m._ufl_handler_name_: i for i, m in enumerate(modifier_precedence)}
def balance_modified_terminal(expr):
@@ -44,10 +55,9 @@ def balance_modified_terminal(expr):
assert expr._ufl_is_terminal_
# Apply modifiers in order
- layers = sorted(
- layers[:-1], key=lambda e: modifier_precedence[e._ufl_handler_name_])
+ layers = sorted(layers[:-1], key=lambda e: modifier_precedence[e._ufl_handler_name_])
for op in layers:
- ops = (expr, ) + op.ufl_operands[1:]
+ ops = (expr,) + op.ufl_operands[1:]
expr = op._ufl_expr_reconstruct_(*ops)
# Preserve id if nothing has changed
diff --git a/ufl/algorithms/change_to_reference.py b/ufl/algorithms/change_to_reference.py
index ed5c22ca2..17b106ad7 100644
--- a/ufl/algorithms/change_to_reference.py
+++ b/ufl/algorithms/change_to_reference.py
@@ -1,4 +1,4 @@
-"""Algorithm for replacing gradients in an expression with reference gradients and coordinate mappings."""
+"""Algorithm for replacing gradients in an expression."""
# Copyright (C) 2008-2016 Martin Sandve Alnæs
#
@@ -18,14 +18,17 @@
"""
# Some notes:
-# Below, let v_i mean physical coordinate of vertex i and V_i mean the reference cell coordinate of the same vertex.
+# Below, let v_i mean physical coordinate of vertex i and V_i mean the
+reference cell coordinate of the same vertex.
-# Add a type for CellVertices? Note that vertices must be computed in linear cell cases!
-triangle_vertices[i,j] = component j of vertex i, following ufc numbering conventions
+# Add a type for CellVertices? Note that vertices must be computed in
+linear cell cases! triangle_vertices[i,j] = component j of vertex i,
+following ufc numbering conventions
-# DONE Add a type for CellEdgeLengths? Note that these are only easy to define in the linear cell case!
+# DONE Add a type for CellEdgeLengths? Note that these are only easy to
+define in the linear cell case!
triangle_edge_lengths = [v1v2, v0v2, v0v1] # shape (3,)
tetrahedron_edge_lengths = [v0v1, v0v2, v0v3, v1v2, v1v3, v2v3] # shape (6,)
@@ -33,7 +36,8 @@
# DONE Here's how to compute edge lengths from the Jacobian:
J =[ [dx0/dX0, dx0/dX1],
[dx1/dX0, dx1/dX1] ]
-# First compute the edge vector, which is constant for each edge: the vector from one vertex to the other
+# First compute the edge vector, which is constant for each edge: the
+vector from one vertex to the other
reference_edge_vector_0 = V2 - V1 # Example! Add a type ReferenceEdgeVectors?
# Then apply J to it and take the length of the resulting vector, this is generic for affine cells
edge_length_i = || dot(J, reference_edge_vector_i) ||
@@ -121,18 +125,18 @@ def grad(self, o):
# Peel off the Grads and count them, and get restriction if
# it's between the grad and the terminal
ngrads = 0
- restricted = ''
+ restricted = ""
rv = False
while not o._ufl_is_terminal_:
if isinstance(o, Grad):
- o, = o.ufl_operands
+ (o,) = o.ufl_operands
ngrads += 1
elif isinstance(o, Restricted):
restricted = o.side()
- o, = o.ufl_operands
+ (o,) = o.ufl_operands
elif isinstance(o, ReferenceValue):
rv = True
- o, = o.ufl_operands
+ (o,) = o.ufl_operands
else:
raise ValueError(f"Invalid type {o._ufl_class_.__name__}")
f = o
@@ -150,7 +154,9 @@ def grad(self, o):
# Create some new indices
ii = indices(len(f.ufl_shape)) # Indices to get to the scalar component of f
- jj = indices(ngrads) # Indices to sum over the local gradient axes with the inverse Jacobian
+ jj = indices(
+ ngrads
+ ) # Indices to sum over the local gradient axes with the inverse Jacobian
kk = indices(ngrads) # Indices for the leftover inverse Jacobian axes
# Preserve restricted property
@@ -182,7 +188,9 @@ def grad(self, o):
jinv_lgrad_f = f
for foo in range(ngrads):
- ii = indices(len(jinv_lgrad_f.ufl_shape)) # Indices to get to the scalar component of f
+ ii = indices(
+ len(jinv_lgrad_f.ufl_shape)
+ ) # Indices to get to the scalar component of f
j, k = indices(2)
lgrad = ReferenceGrad(jinv_lgrad_f)
@@ -199,13 +207,16 @@ def reference_grad(self, o):
def coefficient_derivative(self, o):
"""Apply to coefficient_derivative."""
- raise ValueError("Coefficient derivatives should be expanded before applying change to reference grad.")
+ raise ValueError(
+ "Coefficient derivatives should be expanded before applying change to reference grad."
+ )
def change_to_reference_grad(e):
"""Change Grad objects in expression to products of JacobianInverse and ReferenceGrad.
- Assumes the expression is preprocessed or at least that derivatives have been expanded.
+ Assumes the expression is preprocessed or at least that derivatives
+ have been expanded.
Args:
e: An Expr or Form.
diff --git a/ufl/algorithms/check_arities.py b/ufl/algorithms/check_arities.py
index c93727ad8..e1d9b366b 100644
--- a/ufl/algorithms/check_arities.py
+++ b/ufl/algorithms/check_arities.py
@@ -1,5 +1,7 @@
"""Check arities."""
+
from itertools import chain
+from typing import Tuple
from ufl.classes import Argument, Zero
from ufl.corealg.map_dag import map_expr_dag
@@ -9,12 +11,14 @@
class ArityMismatch(BaseException):
"""Arity mismatch exception."""
+
pass
-def _afmt(atuple):
+def _afmt(atuple: Tuple[Argument, bool]) -> str:
"""Return a string representation of an arity tuple."""
- return tuple(f"conj({arg})" if conj else str(arg) for arg, conj in atuple)
+ arg, conj = atuple
+ return f"conj({arg})" if conj else str(arg)
class ArityChecker(MultiFunction):
@@ -41,8 +45,10 @@ def nonlinear_operator(self, o):
# way we know of:
for t in traverse_unique_terminals(o):
if t._ufl_typecode_ == Argument._ufl_typecode_:
- raise ArityMismatch(f"Applying nonlinear operator {o._ufl_class_.__name__} to "
- f"expression depending on form argument {t}.")
+ raise ArityMismatch(
+ f"Applying nonlinear operator {o._ufl_class_.__name__} to "
+ f"expression depending on form argument {t}."
+ )
return self._et
expr = nonlinear_operator
@@ -50,7 +56,9 @@ def nonlinear_operator(self, o):
def sum(self, o, a, b):
"""Apply to sum."""
if a != b:
- raise ArityMismatch(f"Adding expressions with non-matching form arguments {_afmt(a)} vs {_afmt(b)}.")
+ raise ArityMismatch(
+ f"Adding expressions with non-matching form arguments {_afmt(a)} vs {_afmt(b)}."
+ )
return a
def division(self, o, a, b):
@@ -67,15 +75,19 @@ def product(self, o, a, b):
anumbers = set(x[0].number() for x in a)
for x in b:
if x[0].number() in anumbers:
- raise ArityMismatch("Multiplying expressions with overlapping form argument number "
- f"{x[0].number()}, argument is {_afmt(x)}.")
+ raise ArityMismatch(
+ "Multiplying expressions with overlapping form argument number "
+ f"{x[0].number()}, argument is {_afmt(x)}."
+ )
# Combine argument lists
c = tuple(sorted(set(a + b), key=lambda x: (x[0].number(), x[0].part())))
# Check that we don't have any arguments shared between a
# and b
if len(c) != len(a) + len(b) or len(c) != len({x[0] for x in c}):
- raise ArityMismatch("Multiplying expressions with overlapping form arguments "
- f"{_afmt(a)} vs {_afmt(b)}.")
+ raise ArityMismatch(
+ "Multiplying expressions with overlapping form arguments "
+ f"{_afmt(a)} vs {_afmt(b)}."
+ )
# It's fine for argument parts to overlap
return c
elif a:
@@ -139,8 +151,10 @@ def conditional(self, o, c, a, b):
else:
# Do not allow e.g. conditional(c, test, trial),
# conditional(c, test, nonzeroconstant)
- raise ArityMismatch("Conditional subexpressions with non-matching form arguments "
- f"{_afmt(a)} vs {_afmt(b)}.")
+ raise ArityMismatch(
+ "Conditional subexpressions with non-matching form arguments "
+ f"{_afmt(a)} vs {_afmt(b)}."
+ )
def linear_indexed_type(self, o, a, i):
"""Apply to linear_indexed_type."""
@@ -161,8 +175,10 @@ def list_tensor(self, o, *ops):
if () in numbers: # Allow e.g. but not
numbers.remove(())
if len(numbers) > 1:
- raise ArityMismatch("Listtensor components must depend on the same argument numbers, "
- f"found {numbers}.")
+ raise ArityMismatch(
+ "Listtensor components must depend on the same argument numbers, "
+ f"found {numbers}."
+ )
# Allow different parts with the same number
return tuple(sorted(args, key=lambda x: (x[0].number(), x[0].part())))
@@ -173,8 +189,7 @@ def list_tensor(self, o, *ops):
def check_integrand_arity(expr, arguments, complex_mode=False):
"""Check the arity of an integrand."""
- arguments = tuple(sorted(set(arguments),
- key=lambda x: (x.number(), x.part())))
+ arguments = tuple(sorted(set(arguments), key=lambda x: (x.number(), x.part())))
rules = ArityChecker(arguments)
arg_tuples = map_expr_dag(rules, expr, compress=False)
args = tuple(a[0] for a in arg_tuples)
diff --git a/ufl/algorithms/check_restrictions.py b/ufl/algorithms/check_restrictions.py
index df75e279e..061935981 100644
--- a/ufl/algorithms/check_restrictions.py
+++ b/ufl/algorithms/check_restrictions.py
@@ -28,7 +28,7 @@ def restricted(self, o):
if self.current_restriction is not None:
raise ValueError("Not expecting twice restricted expression.")
self.current_restriction = o._side
- e, = o.ufl_operands
+ (e,) = o.ufl_operands
self.visit(e)
self.current_restriction = None
diff --git a/ufl/algorithms/checks.py b/ufl/algorithms/checks.py
index 9257b160d..a25ba9ec5 100644
--- a/ufl/algorithms/checks.py
+++ b/ufl/algorithms/checks.py
@@ -10,11 +10,13 @@
# Modified by Mehdi Nikbakht, 2010.
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 classes
from ufl.core.expr import ufl_err_str
from ufl.corealg.traversal import traverse_unique_terminals
@@ -22,7 +24,9 @@
from ufl.form import Form
-def validate_form(form): # TODO: Can we make this return a list of errors instead of raising exception?
+def validate_form(
+ form,
+): # TODO: Can we make this return a list of errors instead of raising exception?
"""Performs all implemented validations on a form. Raises exception if something fails."""
errors = []
@@ -37,9 +41,11 @@ def validate_form(form): # TODO: Can we make this return a list of errors inste
# errors.append("Form is not multilinear in arguments.")
# FIXME DOMAIN: Add check for consistency between domains somehow
- domains = set(extract_unique_domain(t)
- for e in iter_expressions(form)
- for t in traverse_unique_terminals(e)) - {None}
+ domains = set(
+ extract_unique_domain(t)
+ for e in iter_expressions(form)
+ for t in traverse_unique_terminals(e)
+ ) - {None}
if not domains:
errors.append("Missing domain definition in form.")
@@ -61,8 +67,7 @@ def validate_form(form): # TODO: Can we make this return a list of errors inste
if c in coefficients:
g = coefficients[c]
if f is not g:
- errors.append("Found different Coefficients with "
- f"same count: {f} and {g}.")
+ errors.append(f"Found different Coefficients with same count: {f} and {g}.")
else:
coefficients[c] = f
@@ -101,4 +106,4 @@ def validate_form(form): # TODO: Can we make this return a list of errors inste
# TODO: Return errors list instead, need to collect messages from
# all validations above first.
if errors:
- raise ValueError("Found errors in validation of form:\n" + '\n\n'.join(errors))
+ raise ValueError("Found errors in validation of form:\n" + "\n\n".join(errors))
diff --git a/ufl/algorithms/comparison_checker.py b/ufl/algorithms/comparison_checker.py
index e61aeecf3..509287e74 100644
--- a/ufl/algorithms/comparison_checker.py
+++ b/ufl/algorithms/comparison_checker.py
@@ -83,19 +83,19 @@ def min_value(self, o, *ops):
def real(self, o, *ops):
"""Apply to real."""
o = self.reuse_if_untouched(o, *ops)
- self.nodetype[o] = 'real'
+ self.nodetype[o] = "real"
return o
def imag(self, o, *ops):
"""Apply to imag."""
o = self.reuse_if_untouched(o, *ops)
- self.nodetype[o] = 'real'
+ self.nodetype[o] = "real"
return o
def sqrt(self, o, *ops):
"""Apply to sqrt."""
o = self.reuse_if_untouched(o, *ops)
- self.nodetype[o] = 'complex'
+ self.nodetype[o] = "complex"
return o
def power(self, o, base, exponent):
@@ -104,28 +104,28 @@ def power(self, o, base, exponent):
try:
# Attempt to diagnose circumstances in which the result must be real.
exponent = float(exponent)
- if self.nodetype[base] == 'real' and int(exponent) == exponent:
- self.nodetype[o] = 'real'
+ if self.nodetype[base] == "real" and int(exponent) == exponent:
+ self.nodetype[o] = "real"
return o
except TypeError:
pass
- self.nodetype[o] = 'complex'
+ self.nodetype[o] = "complex"
return o
def abs(self, o, *ops):
"""Apply to abs."""
o = self.reuse_if_untouched(o, *ops)
- self.nodetype[o] = 'real'
+ self.nodetype[o] = "real"
return o
def terminal(self, term, *ops):
"""Apply to terminal."""
# default terminals to complex, except the ones we *know* are real
if isinstance(term, (RealValue, Zero, Argument, GeometricQuantity)):
- self.nodetype[term] = 'real'
+ self.nodetype[term] = "real"
else:
- self.nodetype[term] = 'complex'
+ self.nodetype[term] = "complex"
return term
def indexed(self, o, expr, multiindex):
diff --git a/ufl/algorithms/compute_form_data.py b/ufl/algorithms/compute_form_data.py
index af0ddf68d..687e8edc4 100644
--- a/ufl/algorithms/compute_form_data.py
+++ b/ufl/algorithms/compute_form_data.py
@@ -1,7 +1,7 @@
"""This module provides the compute_form_data function.
-Form compilers will typically call compute_form_dataprior to code generation to preprocess/simplify a
-raw input form given by a user.
+Form compilers will typically call compute_form_dataprior to code
+generation to preprocess/simplify a raw input form given by a user.
"""
# Copyright (C) 2008-2016 Martin Sandve Alnæs
#
@@ -14,6 +14,7 @@
from ufl.algorithms.analysis import extract_coefficients, extract_sub_elements, unique_tuple
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_geometry_lowering import apply_geometry_lowering
@@ -21,9 +22,13 @@
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, group_form_integrals,
- reconstruct_form_from_integral_data)
+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
@@ -56,8 +61,7 @@ def _compute_element_mapping(form):
# worked around to drop this requirement
# Extract all elements and include subelements of mixed elements
- elements = [obj.ufl_element() for obj in chain(form.arguments(),
- form.coefficients())]
+ elements = [obj.ufl_element() for obj in chain(form.arguments(), form.coefficients())]
elements = extract_sub_elements(elements)
# Try to find a common degree for elements
@@ -66,7 +70,6 @@ def _compute_element_mapping(form):
# Compute element map
element_mapping = {}
for element in elements:
-
# Flag for whether element needs to be reconstructed
reconstruct = False
@@ -74,9 +77,10 @@ def _compute_element_mapping(form):
cell = element.cell
if cell is None:
domains = form.ufl_domains()
- if not all(domains[0].ufl_cell() == d.ufl_cell()
- for d in domains):
- raise ValueError("Cannot replace unknown element cell without unique common cell in form.")
+ if not all(domains[0].ufl_cell() == d.ufl_cell() for d in domains):
+ raise ValueError(
+ "Cannot replace unknown element cell without unique common cell in form."
+ )
cell = domains[0].ufl_cell()
reconstruct = True
@@ -133,8 +137,7 @@ def _compute_form_data_elements(self, arguments, coefficients, domains):
def _check_elements(form_data):
"""Check elements."""
- for element in chain(form_data.unique_elements,
- form_data.unique_sub_elements):
+ for element in chain(form_data.unique_elements, form_data.unique_sub_elements):
if element.cell is None:
raise ValueError(f"Found element with undefined cell: {element}")
@@ -244,10 +247,15 @@ def preprocess_form(form, complex_mode):
def compute_form_data(
- form, do_apply_function_pullbacks=False, do_apply_integral_scaling=False,
- do_apply_geometry_lowering=False, preserve_geometry_types=(),
- do_apply_default_restrictions=True, do_apply_restrictions=True,
- do_estimate_degrees=True, do_append_everywhere_integrals=True,
+ form,
+ do_apply_function_pullbacks=False,
+ do_apply_integral_scaling=False,
+ do_apply_geometry_lowering=False,
+ preserve_geometry_types=(),
+ do_apply_default_restrictions=True,
+ do_apply_restrictions=True,
+ do_estimate_degrees=True,
+ do_append_everywhere_integrals=True,
complex_mode=False,
):
"""Compute form data.
@@ -273,8 +281,11 @@ def compute_form_data(
# TODO: Refactor this, it's rather opaque what this does
# TODO: Is self.original_form.ufl_domains() right here?
# It will matter when we start including 'num_domains' in ufc form.
- form = group_form_integrals(form, self.original_form.ufl_domains(),
- do_append_everywhere_integrals=do_append_everywhere_integrals)
+ form = group_form_integrals(
+ form,
+ self.original_form.ufl_domains(),
+ do_append_everywhere_integrals=do_append_everywhere_integrals,
+ )
# Estimate polynomial degree of integrands now, before applying
# any pullbacks and geometric lowering. Otherwise quad degrees
@@ -350,17 +361,18 @@ def compute_form_data(
reduced_coefficients_set = set()
for itg_data in self.integral_data:
reduced_coefficients_set.update(itg_data.integral_coefficients)
- self.reduced_coefficients = sorted(reduced_coefficients_set,
- key=lambda c: c.count())
+ self.reduced_coefficients = sorted(reduced_coefficients_set, key=lambda c: c.count())
self.num_coefficients = len(self.reduced_coefficients)
- self.original_coefficient_positions = [i for i, c in enumerate(self.original_form.coefficients())
- if c in self.reduced_coefficients]
+ self.original_coefficient_positions = [
+ i for i, c in enumerate(self.original_form.coefficients()) if c in self.reduced_coefficients
+ ]
# Store back into integral data which form coefficients are used
# by each integral
for itg_data in self.integral_data:
- itg_data.enabled_coefficients = [bool(coeff in itg_data.integral_coefficients)
- for coeff in self.reduced_coefficients]
+ itg_data.enabled_coefficients = [
+ bool(coeff in itg_data.integral_coefficients) for coeff in self.reduced_coefficients
+ ]
# --- Collect some trivial data
@@ -384,17 +396,19 @@ def compute_form_data(
# Mappings from elements and coefficients that reside in form to
# objects with canonical numbering as well as completed cells and
# elements
- renumbered_coefficients, function_replace_map = \
- _build_coefficient_replace_map(self.reduced_coefficients,
- self.element_replace_map)
+ renumbered_coefficients, function_replace_map = _build_coefficient_replace_map(
+ self.reduced_coefficients, self.element_replace_map
+ )
self.function_replace_map = function_replace_map
# --- Store various lists of elements and sub elements (adds
# members to self)
- _compute_form_data_elements(self,
- self.original_form.arguments(),
- renumbered_coefficients,
- self.original_form.ufl_domains())
+ _compute_form_data_elements(
+ self,
+ self.original_form.arguments(),
+ renumbered_coefficients,
+ self.original_form.ufl_domains(),
+ )
# --- Store number of domains for integral types
# TODO: Group this by domain first. For now keep a backwards
diff --git a/ufl/algorithms/coordinate_derivative_helpers.py b/ufl/algorithms/coordinate_derivative_helpers.py
index 77b1c19bf..852b655c2 100644
--- a/ufl/algorithms/coordinate_derivative_helpers.py
+++ b/ufl/algorithms/coordinate_derivative_helpers.py
@@ -1,4 +1,4 @@
-"""This module provides the necessary tools to strip away and reattach coordinate derivatives.
+"""Tools to strip away and reattach coordinate derivatives.
This is used in compute_form_data.
"""
@@ -64,7 +64,10 @@ def strip_coordinate_derivatives(integrals):
coordinate_derivatives = []
def take_top_coordinate_derivatives(o):
- """Grab all coordinate derivatives and store them, so that we can apply them later again."""
+ """Get all coordinate derivatives and store them.
+
+ So we can apply them later again.
+ """
o_ = o.ufl_operands
if isinstance(o, CoordinateDerivative):
coordinate_derivatives.append((o_[1], o_[2], o_[3]))
diff --git a/ufl/algorithms/domain_analysis.py b/ufl/algorithms/domain_analysis.py
index 3a11b123a..19e0f4e3c 100644
--- a/ufl/algorithms/domain_analysis.py
+++ b/ufl/algorithms/domain_analysis.py
@@ -11,7 +11,11 @@
from collections import defaultdict
import ufl
-from ufl.algorithms.coordinate_derivative_helpers import attach_coordinate_derivatives, strip_coordinate_derivatives
+from ufl.algorithms.coordinate_derivative_helpers import (
+ attach_coordinate_derivatives,
+ strip_coordinate_derivatives,
+)
+from ufl.algorithms.renumbering import renumber_indices
from ufl.form import Form
from ufl.integral import Integral
from ufl.protocols import id_or_none
@@ -22,17 +26,22 @@
class IntegralData(object):
"""Utility class.
- This class has members (domain, integral_type, subdomain_id, integrals, metadata),
- where metadata is an empty dictionary that may be used for
- associating metadata with each object.
+ This class has members (domain, integral_type, subdomain_id,
+ integrals, metadata), where metadata is an empty dictionary that may
+ be used for associating metadata with each object.
"""
- __slots__ = ('domain', 'integral_type', 'subdomain_id',
- 'integrals', 'metadata',
- 'integral_coefficients',
- 'enabled_coefficients')
- def __init__(self, domain, integral_type, subdomain_id, integrals,
- metadata):
+ __slots__ = (
+ "domain",
+ "integral_type",
+ "subdomain_id",
+ "integrals",
+ "metadata",
+ "integral_coefficients",
+ "enabled_coefficients",
+ )
+
+ def __init__(self, domain, integral_type, subdomain_id, integrals, metadata):
"""Initialise."""
if 1 != len(set(itg.ufl_domain() for itg in integrals)):
raise ValueError("Multiple domains mismatch in integral data.")
@@ -59,21 +68,27 @@ def __init__(self, domain, integral_type, subdomain_id, integrals,
def __lt__(self, other):
"""Check if self is less than other."""
# To preserve behaviour of extract_integral_data:
- return (
- self.integral_type, self.subdomain_id, self.integrals, self.metadata
- ) < (
- other.integral_type, other.subdomain_id, other.integrals, other.metadata
+ return (self.integral_type, self.subdomain_id, self.integrals, self.metadata) < (
+ other.integral_type,
+ other.subdomain_id,
+ other.integrals,
+ other.metadata,
)
def __eq__(self, other):
"""Check for equality."""
# Currently only used for tests:
- return (self.integral_type == other.integral_type and self.subdomain_id == other.subdomain_id and # noqa: W504
- self.integrals == other.integrals and self.metadata == other.metadata)
+ return (
+ self.integral_type == other.integral_type
+ and self.subdomain_id == other.subdomain_id
+ and self.integrals == other.integrals
+ and self.metadata == other.metadata
+ )
def __str__(self):
"""Format as a string."""
- s = f"IntegralData over domain({self.integral_type}, {self.subdomain_id}), with integrals:\n"
+ s = f"IntegralData over domain({self.integral_type}, {self.subdomain_id})"
+ s += " with integrals:\n"
s += "\n\n".join(map(str, self.integrals))
s += "\nand metadata:\n{metadata}"
return s
@@ -81,7 +96,8 @@ def __str__(self):
class ExprTupleKey(object):
"""Tuple comparison helper."""
- __slots__ = ('x',)
+
+ __slots__ = ("x",)
def __init__(self, x):
"""Initialise."""
@@ -141,14 +157,14 @@ def integral_subdomain_ids(integral):
def rearrange_integrals_by_single_subdomains(
- integrals: typing.List[Integral],
- do_append_everywhere_integrals: bool
+ integrals: typing.List[Integral], do_append_everywhere_integrals: bool
) -> typing.Dict[int, typing.List[Integral]]:
"""Rearrange integrals over multiple subdomains to single subdomain integrals.
Args:
integrals: List of integrals
- do_append_everywhere_integrals: Boolean indicating if integrals defined on the whole domain should
+ do_append_everywhere_integrals: Boolean indicating if integrals
+ defined on the whole domain should
just be restricted to the set of input subdomain ids.
Returns:
@@ -188,7 +204,8 @@ def rearrange_integrals_by_single_subdomains(
if do_append_everywhere_integrals:
for subdomain_id in sorted(single_subdomain_integrals.keys()):
single_subdomain_integrals[subdomain_id].append(
- ev_itg.reconstruct(subdomain_id=subdomain_id))
+ ev_itg.reconstruct(subdomain_id=subdomain_id)
+ )
if otherwise_integrals:
single_subdomain_integrals["otherwise"] = otherwise_integrals
@@ -203,8 +220,9 @@ def accumulate_integrands_with_same_metadata(integrals):
integrals: a list of integrals
Returns:
- A list of the form [(integrand0, metadata0), (integrand1, metadata1), ...]
- where integrand0 < integrand1 by the canonical ufl expression ordering criteria.
+ A list of the form [(integrand0, metadata0), (integrand1,
+ metadata1), ...] where integrand0 < integrand1 by the canonical
+ ufl expression ordering criteria.
"""
# Group integrals by compiler data hash
by_cdid = {}
@@ -245,26 +263,16 @@ def build_integral_data(integrals):
itgs = defaultdict(list)
# --- Merge integral data that has the same integrals,
- unique_integrals = defaultdict(tuple)
- metadata_table = defaultdict(dict)
for integral in integrals:
- integrand = integral.integrand()
integral_type = integral.integral_type()
ufl_domain = integral.ufl_domain()
- metadata = integral.metadata()
- meta_hash = hash(canonicalize_metadata(metadata))
- subdomain_id = integral.subdomain_id()
- subdomain_data = id_or_none(integral.subdomain_data())
- if subdomain_id == "everywhere":
- raise ValueError("'everywhere' not a valid subdomain id. Did you forget to call group_form_integrals?")
- unique_integrals[(integral_type, ufl_domain, meta_hash, integrand, subdomain_data)] += (subdomain_id,)
- metadata_table[(integral_type, ufl_domain, meta_hash, integrand, subdomain_data)] = metadata
-
- for integral_data, subdomain_ids in unique_integrals.items():
- (integral_type, ufl_domain, metadata, integrand, subdomain_data) = integral_data
+ subdomain_ids = integral.subdomain_id()
+ if "everywhere" in subdomain_ids:
+ raise ValueError(
+ "'everywhere' not a valid subdomain id. "
+ "Did you forget to call group_form_integrals?"
+ )
- integral = Integral(integrand, integral_type, ufl_domain, subdomain_ids,
- metadata_table[integral_data], subdomain_data)
# Group for integral data (One integral data object for all
# integrals with same domain, itype, (but possibly different metadata).
itgs[(ufl_domain, integral_type, subdomain_ids)].append(integral)
@@ -274,7 +282,8 @@ def build_integral_data(integrals):
def keyfunc(item):
(d, itype, sid), integrals = item
sid_int = tuple(-1 if i == "otherwise" else i for i in sid)
- return (d._ufl_sort_key_(), itype, (type(sid).__name__, ), sid_int)
+ return (d._ufl_sort_key_(), itype, (type(sid).__name__,), sid_int)
+
integral_datas = []
for (d, itype, sid), integrals in sorted(itgs.items(), key=keyfunc):
integral_datas.append(IntegralData(d, itype, sid, integrals, {}))
@@ -287,7 +296,8 @@ def group_form_integrals(form, domains, do_append_everywhere_integrals=True):
Args:
form: the Form to group the integrals of.
domains: an iterable of Domains.
- do_append_everywhere_integrals: Boolean indicating if integrals defined on the whole domain should
+ do_append_everywhere_integrals: Boolean indicating if integrals
+ defined on the whole domain should
just be restricted to the set of input subdomain ids.
Returns:
@@ -309,18 +319,21 @@ def group_form_integrals(form, domains, do_append_everywhere_integrals=True):
# (note: before this call, 'everywhere' is a valid subdomain_id,
# and after this call, 'otherwise' is a valid subdomain_id)
single_subdomain_integrals = rearrange_integrals_by_single_subdomains(
- ddt_integrals, do_append_everywhere_integrals)
+ ddt_integrals, do_append_everywhere_integrals
+ )
for subdomain_id, ss_integrals in sorted_by_key(single_subdomain_integrals):
-
# strip the coordinate derivatives from all integrals
# this yields a list of the form [(coordinate derivative, integral), ...]
stripped_integrals_and_coordderivs = strip_coordinate_derivatives(ss_integrals)
# now group the integrals by the coordinate derivative
def calc_hash(cd):
- return sum(sum(tuple_elem._ufl_compute_hash_()
- for tuple_elem in tuple_) for tuple_ in cd)
+ return sum(
+ sum(tuple_elem._ufl_compute_hash_() for tuple_elem in tuple_)
+ for tuple_ in cd
+ )
+
coordderiv_integrals_dict = {}
for integral, coordderiv in stripped_integrals_and_coordderivs:
coordderivhash = calc_hash(coordderiv)
@@ -335,17 +348,51 @@ def calc_hash(cd):
# apply the CoordinateDerivative again
for cdhash, samecd_integrals in sorted_by_key(coordderiv_integrals_dict):
-
# Accumulate integrands of integrals that share the
# same compiler data
- integrands_and_cds = accumulate_integrands_with_same_metadata(samecd_integrals[1])
+ integrands_and_cds = accumulate_integrands_with_same_metadata(
+ samecd_integrals[1]
+ )
for integrand, metadata in integrands_and_cds:
- integral = Integral(integrand, integral_type, domain,
- subdomain_id, metadata, None)
+ integral = Integral(
+ integrand, integral_type, domain, subdomain_id, metadata, None
+ )
integral = attach_coordinate_derivatives(integral, samecd_integrals[0])
integrals.append(integral)
- return Form(integrals)
+
+ # Group integrals by common integrand
+ # u.dx(0)*dx(1) + u.dx(0)*dx(2) -> u.dx(0)*dx((1,2))
+ # to avoid duplicate kernels generated after geometry lowering
+ unique_integrals = defaultdict(tuple)
+ metadata_table = defaultdict(dict)
+ for integral in integrals:
+ integral_type = integral.integral_type()
+ ufl_domain = integral.ufl_domain()
+ metadata = integral.metadata()
+ meta_hash = hash(canonicalize_metadata(metadata))
+ subdomain_id = integral.subdomain_id()
+ subdomain_data = id_or_none(integral.subdomain_data())
+ integrand = renumber_indices(integral.integrand())
+ unique_integrals[(integral_type, ufl_domain, meta_hash, integrand, subdomain_data)] += (
+ subdomain_id,
+ )
+ metadata_table[(integral_type, ufl_domain, meta_hash, integrand, subdomain_data)] = metadata
+
+ grouped_integrals = []
+ for integral_data, subdomain_ids in unique_integrals.items():
+ (integral_type, ufl_domain, metadata, integrand, subdomain_data) = integral_data
+ integral = Integral(
+ integrand,
+ integral_type,
+ ufl_domain,
+ subdomain_ids,
+ metadata_table[integral_data],
+ subdomain_data,
+ )
+ grouped_integrals.append(integral)
+
+ return Form(grouped_integrals)
def reconstruct_form_from_integral_data(integral_data):
diff --git a/ufl/algorithms/estimate_degrees.py b/ufl/algorithms/estimate_degrees.py
index 334041d3f..da550116c 100644
--- a/ufl/algorithms/estimate_degrees.py
+++ b/ufl/algorithms/estimate_degrees.py
@@ -46,7 +46,8 @@ def constant(self, v):
def geometric_quantity(self, v):
"""Apply to geometric_quantity.
- Some geometric quantities are cellwise constant. Others are nonpolynomial and thus hard to estimate.
+ Some geometric quantities are cellwise constant. Others are
+ nonpolynomial and thus hard to estimate.
"""
if is_cellwise_constant(v):
return 0
@@ -74,7 +75,9 @@ 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().embedded_superdegree # 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.
@@ -95,7 +98,10 @@ def _reduce_degree(self, v, f):
This is used when derivatives are taken. It does not reduce the degree when
TensorProduct elements or quadrilateral elements are involved.
"""
- if isinstance(f, int) and extract_unique_domain(v).ufl_cell().cellname() not in ["quadrilateral", "hexahedron"]:
+ if isinstance(f, int) and extract_unique_domain(v).ufl_cell().cellname() not in [
+ "quadrilateral",
+ "hexahedron",
+ ]:
return max(f - 1, 0)
else:
return f
@@ -284,7 +290,8 @@ def atan2(self, v, a, b):
Using the heuristic:
degree(atan2(const,const)) == 0
degree(atan2(a,b)) == max(degree(a),degree(b))+2
- which can be wildly inaccurate but at least gives a somewhat high integration degree.
+ which can be wildly inaccurate but at least gives a somewhat
+ high integration degree.
"""
if a or b:
return self._add_degrees(v, self._max_degrees(v, a, b), 2)
@@ -297,7 +304,8 @@ def math_function(self, v, a):
Using the heuristic:
degree(sin(const)) == 0
degree(sin(a)) == degree(a)+2
- which can be wildly inaccurate but at least gives a somewhat high integration degree.
+ which can be wildly inaccurate but at least gives a somewhat
+ high integration degree.
"""
if a:
return self._add_degrees(v, a, 2)
@@ -310,7 +318,8 @@ def bessel_function(self, v, nu, x):
Using the heuristic
degree(bessel_*(const)) == 0
degree(bessel_*(x)) == degree(x)+2
- which can be wildly inaccurate but at least gives a somewhat high integration degree.
+ which can be wildly inaccurate but at least gives a somewhat
+ high integration degree.
"""
if x:
return self._add_degrees(v, x, 2)
@@ -337,6 +346,7 @@ def min_value(self, v, a, r):
Same as conditional.
"""
return self._max_degrees(v, a, r)
+
max_value = min_value
def coordinate_derivative(self, v, integrand_degree, b, direction_degree, d):
@@ -357,8 +367,7 @@ def expr_mapping(self, v, *o):
return self._max_degrees(v, *o)
-def estimate_total_polynomial_degree(e, default_degree=1,
- element_replace_map={}):
+def estimate_total_polynomial_degree(e, default_degree=1, element_replace_map={}):
"""Estimate total polynomial degree of integrand.
NB: Although some compound types are supported here,
@@ -366,8 +375,8 @@ def estimate_total_polynomial_degree(e, default_degree=1,
prior to degree estimation. In generic code, this algorithm
should only be applied after preprocessing.
- For coefficients defined on an element with unspecified degree (None),
- the degree is set to the given default degree.
+ For coefficients defined on an element with unspecified degree
+ (None), the degree is set to the given default degree.
"""
de = SumDegreeEstimator(default_degree, element_replace_map)
if isinstance(e, Form):
diff --git a/ufl/algorithms/expand_compounds.py b/ufl/algorithms/expand_compounds.py
deleted file mode 100644
index 97eecc499..000000000
--- a/ufl/algorithms/expand_compounds.py
+++ /dev/null
@@ -1,21 +0,0 @@
-"""Algorithm for expanding compound expressions into equivalent representations using basic operators."""
-
-# 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 Anders Logg, 2009-2010
-
-import warnings
-
-from ufl.algorithms.apply_algebra_lowering import apply_algebra_lowering
-
-
-def expand_compounds(e):
- """Expand compounds."""
- warnings.warn("The use of expand_compounds is deprecated and will be removed after December 2023. "
- "Please, use apply_algebra_lowering directly instead", FutureWarning)
-
- return apply_algebra_lowering(e)
diff --git a/ufl/algorithms/expand_indices.py b/ufl/algorithms/expand_indices.py
index 1523bd4c4..316998341 100644
--- a/ufl/algorithms/expand_indices.py
+++ b/ufl/algorithms/expand_indices.py
@@ -49,7 +49,7 @@ def form_argument(self, x):
if sh == ():
return x
else:
- e = x.ufl_element()
+ space = x.ufl_function_space()
r = len(sh)
# Get component
@@ -58,8 +58,8 @@ def form_argument(self, x):
raise ValueError("Component size mismatch.")
# Map it through an eventual symmetry mapping
- if len(e.components) > 1:
- c = min(i for i, j in e.components.items() if j == e.components[c])
+ if len(space.components) > 1:
+ c = min(i for i, j in space.components.items() if j == space.components[c])
if r != len(c):
raise ValueError("Component size mismatch after symmetry mapping.")
@@ -133,7 +133,7 @@ def index_sum(self, x):
"""Apply to index_sum."""
ops = []
summand, multiindex = x.ufl_operands
- index, = multiindex
+ (index,) = multiindex
# TODO: For the list tensor purging algorithm, do something like:
# if index not in self._to_expand:
@@ -222,7 +222,7 @@ def list_tensor(self, x):
def grad(self, x):
"""Apply to grad."""
- f, = x.ufl_operands
+ (f,) = x.ufl_operands
if not isinstance(f, (Terminal, Grad)):
raise ValueError("Expecting expand_derivatives to have been applied.")
# No need to visit child as long as it is on the form [Grad]([Grad](terminal))
diff --git a/ufl/algorithms/formdata.py b/ufl/algorithms/formdata.py
index 6f1048aec..f6bdf4c14 100644
--- a/ufl/algorithms/formdata.py
+++ b/ufl/algorithms/formdata.py
@@ -20,9 +20,11 @@ def __init__(self):
def __str__(self):
"""Return formatted summary of form data."""
types = sorted(self.max_subdomain_ids.keys())
- geometry = (("Geometric dimension", self.geometric_dimension), )
- subdomains = tuple((f"Number of {integral_type} subdomains", self.max_subdomain_ids[integral_type])
- for integral_type in types)
+ geometry = (("Geometric dimension", self.geometric_dimension),)
+ subdomains = tuple(
+ (f"Number of {integral_type} subdomains", self.max_subdomain_ids[integral_type])
+ for integral_type in types
+ )
functions = (
# Arguments
("Rank", self.rank),
diff --git a/ufl/algorithms/formfiles.py b/ufl/algorithms/formfiles.py
index 7927f3a11..a93383275 100644
--- a/ufl/algorithms/formfiles.py
+++ b/ufl/algorithms/formfiles.py
@@ -37,8 +37,15 @@ def __init__(self):
def __bool__(self):
"""Convert to a bool."""
- return bool(self.elements or self.coefficients or self.forms or self.expressions or # noqa: W504
- self.object_names or self.object_by_name or self.reserved_objects)
+ return bool(
+ self.elements
+ or self.coefficients
+ or self.forms
+ or self.expressions
+ or self.object_names
+ or self.object_by_name
+ or self.reserved_objects
+ )
__nonzero__ = __bool__
@@ -59,9 +66,9 @@ def match(line):
for i in range(min(2, len(lines))):
m = match(lines[i])
if m:
- encoding, = m.groups()
+ (encoding,) = m.groups()
# Drop encoding line
- lines = lines[:i] + lines[i + 1:]
+ lines = lines[:i] + lines[i + 1 :]
break
else:
# Default to utf-8 (works for ascii files
@@ -107,7 +114,9 @@ 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, (AbstractFiniteElement, 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
@@ -121,6 +130,7 @@ def interpret_ufl_namespace(namespace):
def get_form(name):
form = ufd.object_by_name.get(name)
return form if isinstance(form, Form) else None
+
a_form = get_form("a")
L_form = get_form("L")
M_form = get_form("M")
@@ -149,7 +159,9 @@ 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)}''.")
+ raise ValueError(
+ f"Expecting 'elements' to be a list or tuple, not '{type(ufd.elements)}''."
+ )
if not all(isinstance(e, AbstractFiniteElement) for e in ufd.elements):
raise ValueError("Expecting 'elements' to be a list of AbstractFiniteElement instances.")
@@ -159,7 +171,9 @@ def get_form(name):
# Validate types
if not isinstance(ufd.coefficients, (list, tuple)):
- raise ValueError(f"Expecting 'coefficients' to be a list or tuple, not '{type(ufd.coefficients)}'.")
+ raise ValueError(
+ f"Expecting 'coefficients' to be a list or tuple, not '{type(ufd.coefficients)}'."
+ )
if not all(isinstance(e, Coefficient) for e in ufd.coefficients):
raise ValueError("Expecting 'coefficients' to be a list of Coefficient instances.")
@@ -168,7 +182,9 @@ def get_form(name):
# Validate types
if not isinstance(ufd.expressions, (list, tuple)):
- raise ValueError(f"Expecting 'expressions' to be a list or tuple, not '{type(ufd.expressions)}'.")
+ raise ValueError(
+ f"Expecting 'expressions' to be a list or tuple, not '{type(ufd.expressions)}'."
+ )
if not all(isinstance(e[0], Expr) for e in ufd.expressions):
raise ValueError("Expecting 'expressions' to be a list of Expr instances.")
diff --git a/ufl/algorithms/formsplitter.py b/ufl/algorithms/formsplitter.py
index ef9b2bbef..e9fefaf68 100644
--- a/ufl/algorithms/formsplitter.py
+++ b/ufl/algorithms/formsplitter.py
@@ -1,6 +1,6 @@
"""Extract part of a form in a mixed FunctionSpace."""
-# Copyright (C) 2016 Chris Richardson and Lawrence Mitchell
+# Copyright (C) 2016-2024 Chris Richardson and Lawrence Mitchell
#
# This file is part of UFL (https://www.fenicsproject.org)
#
@@ -8,8 +8,11 @@
#
# Modified by Cecile Daversin-Catty, 2018
-from ufl.algorithms.map_integrands import map_integrand_dags
+from typing import Optional
+
+from ufl.algorithms.map_integrands import map_expr_dag, map_integrand_dags
from ufl.argument import Argument
+from ufl.classes import FixedIndex, ListTensor
from ufl.constantvalue import Zero
from ufl.corealg.multifunction import MultiFunction
from ufl.functionspace import FunctionSpace
@@ -27,32 +30,22 @@ def split(self, form, ix, iy=0):
def argument(self, obj):
"""Apply to argument."""
- if (obj.part() is not None):
+ if obj.part() is not None:
# Mixed element built from MixedFunctionSpace,
# whose sub-function spaces are indexed by obj.part()
- if len(obj.ufl_shape) == 0:
- if (obj.part() == self.idx[obj.number()]):
- return obj
- else:
- return Zero()
+ if obj.part() == self.idx[obj.number()]:
+ return obj
else:
- indices = [()]
- for m in obj.ufl_shape:
- indices = [(k + (j,)) for k in indices for j in range(m)]
-
- if (obj.part() == self.idx[obj.number()]):
- return as_vector([obj[j] for j in indices])
- else:
- return as_vector([Zero() for j in indices])
+ return Zero(obj.ufl_shape)
else:
# Mixed element built from MixedElement,
# whose sub-elements need their function space to be created
Q = obj.ufl_function_space()
dom = Q.ufl_domain()
- sub_elements = obj.ufl_element().sub_elements()
+ sub_elements = obj.ufl_element().sub_elements
# If not a mixed element, do nothing
- if (len(sub_elements) == 0):
+ if len(sub_elements) == 0:
return obj
args = []
@@ -64,42 +57,101 @@ def argument(self, obj):
for m in a.ufl_shape:
indices = [(k + (j,)) for k in indices for j in range(m)]
- if (i == self.idx[obj.number()]):
+ if i == self.idx[obj.number()]:
args += [a[j] for j in indices]
else:
args += [Zero() for j in indices]
return as_vector(args)
+ def indexed(self, o, child, multiindex):
+ """Extract indexed entry if multindices are fixed.
+
+ This avoids tensors like (v_0, 0)[1] to be created.
+ """
+ indices = multiindex.indices()
+ if isinstance(child, ListTensor) and all(isinstance(i, FixedIndex) for i in indices):
+ if len(indices) == 1:
+ return child.ufl_operands[indices[0]._value]
+ else:
+ return ListTensor(*(child.ufl_operands[i._value] for i in multiindex.indices()))
+ return self.expr(o, child, multiindex)
+
def multi_index(self, obj):
"""Apply to multi_index."""
return obj
+ def restricted(self, o):
+ """Apply to a restricted function."""
+ # If we hit a restriction first apply form splitter to argument, then check for zero
+ op_split = map_expr_dag(self, o.ufl_operands[0])
+ if isinstance(op_split, Zero):
+ return op_split
+ else:
+ return op_split(o._side)
+
expr = MultiFunction.reuse_if_untouched
-def extract_blocks(form, i=None, j=None):
- """Extract blocks."""
+def extract_blocks(form, i: Optional[int] = None, j: Optional[None] = None):
+ """Extract blocks of a form.
+
+ If arity is 0, returns the form.
+ If arity is 1, return the ith block. If ``i`` is ``None``, return all blocks.
+ If arity is 2, return the ``(i,j)`` entry. If ``j`` is ``None``, return the ith row.
+
+ If neither `i` nor `j` are set, return all blocks (as a scalar, vector or tensor).
+
+ Args:
+ form: A form
+ i: Index of the block to extract. If set to ``None``, ``j`` must be None.
+ j: Index of the block to extract.
+ """
+ if i is None and j is not None:
+ raise RuntimeError(f"Cannot extract block with {j=} and {i=}.")
+
fs = FormSplitter()
arguments = form.arguments()
- forms = []
-
numbers = tuple(sorted(set(a.number() for a in arguments)))
arity = len(numbers)
- parts = tuple(sorted(set(a.part() for a in arguments)))
assert arity <= 2
-
if arity == 0:
- return (form, )
+ return (form,)
+
+ # If mixed element, each argument has no sub-elements
+ parts = tuple(sorted(set(part for a in arguments if (part := a.part()) is not None)))
+ if parts == ():
+ if i is None and j is None:
+ num_sub_elements = arguments[0].ufl_element().num_sub_elements
+ forms = []
+ for pi in range(num_sub_elements):
+ form_i = []
+ for pj in range(num_sub_elements):
+ f = fs.split(form, pi, pj)
+ if f.empty():
+ form_i.append(None)
+ else:
+ form_i.append(f)
+ forms.append(tuple(form_i))
+ return tuple(forms)
+ else:
+ return fs.split(form, i, j)
- for pi in parts:
+ # If mixed function space, each argument has sub-elements
+ forms = []
+ num_parts = len(parts)
+ for pi in range(num_parts):
+ form_i = []
if arity > 1:
- for pj in parts:
+ for pj in range(num_parts):
f = fs.split(form, pi, pj)
if f.empty():
- forms.append(None)
+ form_i.append(None)
else:
- forms.append(f)
+ if (num_args := len(f.arguments())) != 2:
+ raise RuntimeError(f"Expected 2 arguments, got {num_args}")
+ form_i.append(f)
+ forms.append(tuple(form_i))
else:
f = fs.split(form, pi)
if f.empty():
@@ -111,11 +163,16 @@ def extract_blocks(form, i=None, j=None):
forms_tuple = tuple(forms)
except TypeError:
# Only one form returned
- forms_tuple = (forms, )
-
+ forms_tuple = (forms,)
if i is not None:
+ if (num_rows := len(forms_tuple)) <= i:
+ raise RuntimeError(f"Cannot extract block {i} from form with {num_rows} blocks.")
if arity > 1 and j is not None:
- return forms_tuple[i * len(parts) + j]
+ if (num_cols := len(forms_tuple[i])) <= j:
+ raise RuntimeError(
+ f"Cannot extract block {i},{j} from form with {num_rows}x{num_cols} blocks."
+ )
+ return forms_tuple[i][j]
else:
return forms_tuple[i]
else:
diff --git a/ufl/algorithms/formtransformations.py b/ufl/algorithms/formtransformations.py
index 58d168c14..20a6a1743 100644
--- a/ufl/algorithms/formtransformations.py
+++ b/ufl/algorithms/formtransformations.py
@@ -1,4 +1,4 @@
-"""This module defines utilities for transforming complete Forms into new related Forms."""
+"""Utilities for transforming complete Forms into new related Forms."""
# Copyright (C) 2008-2016 Martin Sandve Alnæs
#
@@ -14,6 +14,7 @@
from logging import debug
from ufl.algebra import Conj
+
# Other algorithms:
from ufl.algorithms.map_integrands import map_integrands
from ufl.algorithms.replace import replace
@@ -21,6 +22,7 @@
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
@@ -123,7 +125,6 @@ def sum(self, x):
original_terms = x.ufl_operands
assert len(original_terms) == 2
for term in original_terms:
-
# Visit this term in the sum
part, term_provides = self.visit(term)
@@ -145,8 +146,7 @@ def sum(self, x):
# 3. Return the terms that provide the biggest set
most_provided = frozenset()
- for (provideds, parts) in parts_that_provide.items(): # TODO: Just sort instead?
-
+ for provideds, parts in parts_that_provide.items(): # TODO: Just sort instead?
# Throw error if size of sets are equal (and not zero)
if len(provideds) == len(most_provided) and len(most_provided):
raise ValueError("Don't know what to do with sums with different Arguments.")
@@ -158,7 +158,7 @@ def sum(self, x):
if len(terms) == 2:
x = self.reuse_if_possible(x, *terms)
else:
- x, = terms
+ (x,) = terms
return (x, most_provided)
@@ -172,7 +172,6 @@ def product(self, x, *ops):
factors = []
for factor, factor_provides in ops:
-
# If any factor is zero, return
if isinstance(factor, Zero):
return (zero_expr(x), set())
@@ -202,7 +201,9 @@ def division(self, x):
# Check for Arguments in the denominator
if _expr_has_terminal_types(denominator, Argument):
- raise ValueError(f"Found Argument in denominator of {ufl_err_str(x)}, this is an invalid expression.")
+ raise ValueError(
+ f"Found Argument in denominator of {ufl_err_str(x)}, this is an invalid expression."
+ )
# Visit numerator
numerator_parts, provides = self.visit(numerator)
@@ -281,18 +282,20 @@ def list_tensor(self, x, *ops):
# Extract the most arguments provided by any of the components
most_provides = ops[0][1]
- for (component, provides) in ops:
- if (provides - most_provides):
+ for component, provides in ops:
+ if provides - most_provides:
most_provides = provides
# Check that all components either provide the same arguments
# or vanish. (This check is here b/c it is not obvious what to
# return if the components provide different arguments, at
# least with the current transformer design.)
- for (component, provides) in ops:
- if (provides != most_provides and not isinstance(component, Zero)):
- raise ValueError("PartExtracter does not know how to handle list_tensors with "
- "non-zero components providing fewer arguments")
+ for component, provides in ops:
+ if provides != most_provides and not isinstance(component, Zero):
+ raise ValueError(
+ "PartExtracter does not know how to handle list_tensors with "
+ "non-zero components providing fewer arguments"
+ )
# Return components
components = [op[0] for op in ops]
@@ -327,6 +330,7 @@ def _transform(e):
if provides == sub_arguments:
return e
return Zero()
+
return map_integrands(_transform, form)
@@ -341,7 +345,6 @@ def compute_form_arities(form):
arities = set()
for arity in range(len(arguments) + 1):
-
# Compute parts with arity "arity"
parts = compute_form_with_arity(form, arity, arguments)
@@ -432,13 +435,17 @@ def compute_energy_norm(form, coefficient):
U = u.ufl_function_space()
V = v.ufl_function_space()
if U != V:
- raise ValueError(f"Expecting equal finite elements for test and trial functions, got '{U}' and '{V}'.")
+ raise ValueError(
+ f"Expecting equal finite elements for test and trial functions, got '{U}' and '{V}'."
+ )
if coefficient is None:
coefficient = Coefficient(V)
else:
if coefficient.ufl_function_space() != U:
- raise ValueError("Trying to compute action of form on a "
- "coefficient in an incompatible element space.")
+ raise ValueError(
+ "Trying to compute action of form on a "
+ "coefficient in an incompatible element space."
+ )
return action(action(form, coefficient), coefficient)
@@ -462,10 +469,8 @@ def compute_form_adjoint(form, reordered_arguments=None):
raise ValueError("Mistaken assumption in code!")
if reordered_arguments is None:
- reordered_u = Argument(u.ufl_function_space(), number=v.number(),
- part=v.part())
- reordered_v = Argument(v.ufl_function_space(), number=u.number(),
- part=u.part())
+ reordered_u = Argument(u.ufl_function_space(), number=v.number(), part=v.part())
+ reordered_v = Argument(v.ufl_function_space(), number=u.number(), part=u.part())
else:
reordered_u, reordered_v = reordered_arguments
diff --git a/ufl/algorithms/map_integrands.py b/ufl/algorithms/map_integrands.py
index 55266dfb9..0a6da1817 100644
--- a/ufl/algorithms/map_integrands.py
+++ b/ufl/algorithms/map_integrands.py
@@ -1,4 +1,4 @@
-"""Basic algorithms for applying functions to subexpressions."""
+"""Basic algorithms for applying functions to sub-expressions."""
# Copyright (C) 2008-2016 Martin Sandve Alnæs
#
@@ -20,12 +20,18 @@
def map_integrands(function, form, only_integral_type=None):
- """Apply transform(expression) to each integrand expression in form, or to form if it is an Expr."""
+ """Map integrands.
+
+ Apply transform(expression) to each integrand expression in form, or
+ to form if it is an Expr.
+ """
if isinstance(form, Form):
- mapped_integrals = [map_integrands(function, itg, only_integral_type)
- for itg in form.integrals()]
- nonzero_integrals = [itg for itg in mapped_integrals
- if not isinstance(itg.integrand(), Zero)]
+ mapped_integrals = [
+ map_integrands(function, itg, only_integral_type) for itg in form.integrals()
+ ]
+ nonzero_integrals = [
+ itg for itg in mapped_integrals if not isinstance(itg.integrand(), Zero)
+ ]
return Form(nonzero_integrals)
elif isinstance(form, Integral):
itg = form
@@ -34,11 +40,16 @@ def map_integrands(function, form, only_integral_type=None):
else:
return itg
elif isinstance(form, FormSum):
- mapped_components = [map_integrands(function, component, only_integral_type)
- for component in form.components()]
- nonzero_components = [(component, w) for component, w in zip(mapped_components, form.weights())
- # Catch ufl.Zero and ZeroBaseForm
- if component != 0]
+ mapped_components = [
+ map_integrands(function, component, only_integral_type)
+ for component in form.components()
+ ]
+ nonzero_components = [
+ (component, w)
+ for component, w in zip(mapped_components, form.weights())
+ # Catch ufl.Zero and ZeroBaseForm
+ if component != 0
+ ]
# Simplify case with one nonzero component and the corresponding weight is 1
if len(nonzero_components) == 1 and nonzero_components[0][1] == 1:
@@ -59,7 +70,9 @@ def map_integrands(function, form, only_integral_type=None):
# Zeros are caught inside `Action.__new__`
return Action(left, right)
elif isinstance(form, ZeroBaseForm):
- arguments = tuple(map_integrands(function, arg, only_integral_type) for arg in form._arguments)
+ arguments = tuple(
+ map_integrands(function, arg, only_integral_type) for arg in form._arguments
+ )
return ZeroBaseForm(arguments)
elif isinstance(form, (Expr, BaseForm)):
integrand = form
@@ -70,5 +83,6 @@ def map_integrands(function, form, only_integral_type=None):
def map_integrand_dags(function, form, only_integral_type=None, compress=True):
"""Map integrand dags."""
- return map_integrands(lambda expr: map_expr_dag(function, expr, compress),
- form, only_integral_type)
+ return map_integrands(
+ lambda expr: map_expr_dag(function, expr, compress), form, only_integral_type
+ )
diff --git a/ufl/algorithms/remove_complex_nodes.py b/ufl/algorithms/remove_complex_nodes.py
index 7d97a5731..99b646205 100644
--- a/ufl/algorithms/remove_complex_nodes.py
+++ b/ufl/algorithms/remove_complex_nodes.py
@@ -1,4 +1,4 @@
-"""Algorithm for removing conj, real, and imag nodes from a form for when the user is in 'real mode'."""
+"""Remove conj, real, and imag nodes from a form."""
from ufl.algorithms.map_integrands import map_integrand_dags
from ufl.constantvalue import ComplexValue
@@ -7,6 +7,7 @@
class ComplexNodeRemoval(MultiFunction):
"""Replaces complex operator nodes with their children."""
+
expr = MultiFunction.reuse_if_untouched
def conj(self, o, a):
@@ -24,7 +25,7 @@ def imag(self, o, a):
def terminal(self, t, *ops):
"""Apply to terminal."""
if isinstance(t, ComplexValue):
- raise ValueError('Unexpected complex value in real expression.')
+ raise ValueError("Unexpected complex value in real expression.")
else:
return t
@@ -32,8 +33,8 @@ def terminal(self, t, *ops):
def remove_complex_nodes(expr):
"""Replaces complex operator nodes with their children.
- This is called during compute_form_data if the compiler wishes to compile
- real-valued forms. In essence this strips all trace of complex
- support from the preprocessed form.
+ This is called during compute_form_data if the compiler wishes to
+ compile real-valued forms. In essence this strips all trace of
+ complex support from the preprocessed form.
"""
return map_integrand_dags(ComplexNodeRemoval(), expr)
diff --git a/ufl/algorithms/renumbering.py b/ufl/algorithms/renumbering.py
index 303457dd6..0e0408c8d 100644
--- a/ufl/algorithms/renumbering.py
+++ b/ufl/algorithms/renumbering.py
@@ -1,83 +1,57 @@
"""Algorithms for renumbering of counted objects, currently variables and indices."""
-# Copyright (C) 2008-2016 Martin Sandve Alnæs and Anders Logg
+# Copyright (C) 2008-2024 Martin Sandve Alnæs, Anders Logg, Jørgen S. Dokken and Lawrence Mitchell
#
# This file is part of UFL (https://www.fenicsproject.org)
#
# SPDX-License-Identifier: LGPL-3.0-or-later
-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
+from collections import defaultdict
+from itertools import count as _count
+from ufl.algorithms.map_integrands import map_integrand_dags
+from ufl.core.multiindex import Index
+from ufl.corealg.multifunction import MultiFunction
-class VariableRenumberingTransformer(ReuseTransformer):
- """Variable renumbering transformer."""
-
- def __init__(self):
- """Initialise."""
- ReuseTransformer.__init__(self)
- self.variable_map = {}
-
- def variable(self, o):
- """Apply to variable."""
- e, l = o.ufl_operands # noqa: E741
- v = self.variable_map.get(l)
- if v is None:
- e = self.visit(e)
- l2 = Label(len(self.variable_map))
- v = Variable(e, l2)
- self.variable_map[l] = v
- return v
+class IndexRelabeller(MultiFunction):
+ """Renumber indices to have a consistent index numbering starting from 0."""
-class IndexRenumberingTransformer(VariableRenumberingTransformer):
- """Index renumbering transformer.
+ def __init__(self):
+ """Initialize index relabeller with a zero count."""
+ super().__init__()
+ count = _count()
+ self.index_cache = defaultdict(lambda: Index(next(count)))
- This is a poorly designed algorithm. It is used in some tests, please do not use for anything else.
- """
+ expr = MultiFunction.reuse_if_untouched
- def __init__(self):
- """Initialise."""
- VariableRenumberingTransformer.__init__(self)
- self.index_map = {}
+ def multi_index(self, o):
+ """Apply to multi-indices."""
+ return type(o)(
+ tuple(self.index_cache[i] if isinstance(i, Index) else i for i in o.indices())
+ )
def zero(self, o):
"""Apply to zero."""
fi = o.ufl_free_indices
fid = o.ufl_index_dimensions
- mapped_fi = tuple(self.index(Index(count=i)) for i in fi)
- paired_fid = [(mapped_fi[pos], fid[pos]) for pos, a in enumerate(fi)]
- new_fi, new_fid = zip(*tuple(sorted(paired_fid)))
- return Zero(o.ufl_shape, new_fi, new_fid)
-
- def index(self, o):
- """Apply to index."""
- if isinstance(o, FixedIndex):
+ new_indices = [self.index_cache[Index(i)].count() for i in fi]
+ if fi == () and fid == ():
return o
- else:
- c = o._count
- i = self.index_map.get(c)
- if i is None:
- i = Index(count=len(self.index_map))
- self.index_map[c] = i
- return i
+ new_fi, new_fid = zip(*sorted(zip(new_indices, fid), key=lambda x: x[0]))
+ return type(o)(o.ufl_shape, tuple(new_fi), tuple(new_fid))
- def multi_index(self, o):
- """Apply to multi_index."""
- new_indices = tuple(self.index(i) for i in o.indices())
- return MultiIndex(new_indices)
+def renumber_indices(form):
+ """Renumber indices to have a consistent index numbering starting from 0.
-def renumber_indices(expr):
- """Renumber indices."""
- if isinstance(expr, Expr):
- num_free_indices = len(expr.ufl_free_indices)
+ This is useful to avoid multiple kernels for the same integrand,
+ but with different subdomain ids.
- result = apply_transformer(expr, IndexRenumberingTransformer())
+ Args:
+ form: A UFL form, integral or expression.
- if isinstance(expr, Expr):
- if num_free_indices != len(result.ufl_free_indices):
- raise ValueError("The number of free indices left in expression should be invariant w.r.t. renumbering.")
- return result
+ Returns:
+ A new form, integral or expression with renumbered indices.
+ """
+ reindexer = IndexRelabeller()
+ return map_integrand_dags(reindexer, form)
diff --git a/ufl/algorithms/replace.py b/ufl/algorithms/replace.py
index e96ecc577..fbe9cecd0 100644
--- a/ufl/algorithms/replace.py
+++ b/ufl/algorithms/replace.py
@@ -10,7 +10,7 @@
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.classes import BaseForm, CoefficientDerivative
from ufl.constantvalue import as_ufl
from ufl.core.external_operator import ExternalOperator
from ufl.core.interpolate import Interpolate
@@ -28,12 +28,14 @@ def __init__(self, mapping):
# One can replace Coarguments by 1-Forms
def get_shape(x):
"""Get the shape of an object."""
- if isinstance(x, Form):
+ if isinstance(x, BaseForm):
return x.arguments()[0].ufl_shape
return x.ufl_shape
if not all(get_shape(k) == get_shape(v) for k, v in mapping.items()):
- raise ValueError("Replacement expressions must have the same shape as what they replace.")
+ raise ValueError(
+ "Replacement expressions must have the same shape as what they replace."
+ )
def ufl_type(self, o, *args):
"""Replace a ufl_type."""
@@ -88,6 +90,7 @@ def replace(e, mapping):
if has_exact_type(e, CoefficientDerivative):
# Hack to avoid circular dependencies
from ufl.algorithms.ad import expand_derivatives
+
e = expand_derivatives(e)
return map_integrand_dags(Replacer(mapping2), e)
diff --git a/ufl/algorithms/replace_derivative_nodes.py b/ufl/algorithms/replace_derivative_nodes.py
index 9c822fafb..9d373407e 100644
--- a/ufl/algorithms/replace_derivative_nodes.py
+++ b/ufl/algorithms/replace_derivative_nodes.py
@@ -22,18 +22,20 @@ def __init__(self, mapping, **derivative_kwargs):
def coefficient_derivative(self, cd, o, coefficients, arguments, coefficient_derivatives):
"""Apply to coefficient_derivative."""
der_kwargs = self.der_kwargs
- new_coefficients = tuple(self.mapping[c] if c in self.mapping.keys() else c for c in coefficients.ufl_operands)
+ new_coefficients = tuple(
+ self.mapping[c] if c in self.mapping.keys() else c for c in coefficients.ufl_operands
+ )
# Ensure type compatibility for arguments!
- if 'argument' not in der_kwargs.keys():
+ if "argument" not in der_kwargs.keys():
# Argument's number/part can be retrieved from the former coefficient derivative.
arguments = arguments.ufl_operands
new_arguments = ()
for c, a in zip(new_coefficients, arguments):
if isinstance(a, ListTensor):
- a, = extract_arguments(a)
+ (a,) = extract_arguments(a)
new_arguments += (type(a)(c.ufl_function_space(), a.number(), a.part()),)
- der_kwargs.update({'argument': new_arguments})
+ der_kwargs.update({"argument": new_arguments})
return ufl.derivative(o, new_coefficients, **der_kwargs)
@@ -44,8 +46,9 @@ def replace_derivative_nodes(expr, mapping, **derivative_kwargs):
Replaces the variable with respect to which the derivative is taken.
This is called during apply_derivatives to treat delayed derivatives.
- Example: Let u be a Coefficient, N an ExternalOperator independent of u (i.e. N's operands don't depend on u),
- and let uhat and Nhat be Arguments.
+ Example: Let u be a Coefficient, N an ExternalOperator independent
+ of u (i.e. N's operands don't depend on u),
+ and let uhat and Nhat be Arguments.
F = u ** 2 * N * dx
dFdu = derivative(F, u, uhat)
@@ -59,8 +62,8 @@ def replace_derivative_nodes(expr, mapping, **derivative_kwargs):
Args:
expr: An Expr or BaseForm.
mapping: A dict with from:to replacements to perform.
- derivative_kwargs: A dict containing the keyword arguments for derivative
- (i.e. `argument` and `coefficient_derivatives`).
+ derivative_kwargs: A dict containing the keyword arguments for
+ derivative (i.e. `argument` and `coefficient_derivatives`).
"""
mapping2 = dict((k, as_ufl(v)) for (k, v) in mapping.items())
return map_integrand_dags(DerivativeNodeReplacer(mapping2, **derivative_kwargs), expr)
diff --git a/ufl/algorithms/signature.py b/ufl/algorithms/signature.py
index 7f9dca8b8..807fa5c00 100644
--- a/ufl/algorithms/signature.py
+++ b/ufl/algorithms/signature.py
@@ -8,8 +8,18 @@
import hashlib
from ufl.algorithms.domain_analysis import canonicalize_metadata
-from ufl.classes import (Argument, Coefficient, Constant, ConstantValue, ExprList, ExprMapping, GeometricQuantity,
- Index, Label, MultiIndex)
+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
@@ -43,7 +53,6 @@ def compute_terminal_hashdata(expressions, renumbering):
index_numbering = {}
for expression in expressions:
for expr in traverse_unique_terminals(expression):
-
if isinstance(expr, MultiIndex):
# Indices need a canonical numbering for a stable
# signature, thus this algorithm
@@ -131,8 +140,7 @@ def compute_form_signature(form, renumbering): # FIXME: Fix callers
hashdata = []
for integral in integrals:
# Compute hash data for expression, this is the expensive part
- integrand_hashdata = compute_expression_hashdata(integral.integrand(),
- terminal_hashdata)
+ integrand_hashdata = compute_expression_hashdata(integral.integrand(), terminal_hashdata)
domain_hashdata = integral.ufl_domain()._ufl_signature_data_(renumbering)
diff --git a/ufl/algorithms/strip_terminal_data.py b/ufl/algorithms/strip_terminal_data.py
index 4909febe4..a270d3273 100644
--- a/ufl/algorithms/strip_terminal_data.py
+++ b/ufl/algorithms/strip_terminal_data.py
@@ -4,8 +4,18 @@
"""
from ufl.algorithms.replace import replace
-from ufl.classes import (Argument, Coefficient, Constant, Form, FunctionSpace, Integral, Mesh, MeshView,
- MixedFunctionSpace, TensorProductFunctionSpace)
+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
@@ -20,20 +30,17 @@ def __init__(self):
def argument(self, o):
"""Apply to argument."""
- o_new = Argument(strip_function_space(o.ufl_function_space()),
- o.number(), o.part())
+ o_new = Argument(strip_function_space(o.ufl_function_space()), o.number(), o.part())
return self.mapping.setdefault(o, o_new)
def coefficient(self, o):
"""Apply to coefficient."""
- o_new = Coefficient(strip_function_space(o.ufl_function_space()),
- o.count())
+ o_new = Coefficient(strip_function_space(o.ufl_function_space()), o.count())
return self.mapping.setdefault(o, o_new)
def constant(self, o):
"""Apply to constant."""
- o_new = Constant(strip_domain(o.ufl_domain()), o.ufl_shape,
- o.count())
+ o_new = Constant(strip_domain(o.ufl_domain()), o.ufl_shape, o.count())
return self.mapping.setdefault(o, o_new)
expr = MultiFunction.reuse_if_untouched
@@ -102,8 +109,9 @@ def replace_terminal_data(o, mapping):
def strip_function_space(function_space):
"""Return a new function space with all non-UFL information removed."""
if isinstance(function_space, FunctionSpace):
- return FunctionSpace(strip_domain(function_space.ufl_domain()),
- function_space.ufl_element())
+ return FunctionSpace(
+ strip_domain(function_space.ufl_domain()), function_space.ufl_element()
+ )
elif isinstance(function_space, TensorProductFunctionSpace):
subspaces = [strip_function_space(sub) for sub in function_space.ufl_sub_spaces()]
return TensorProductFunctionSpace(*subspaces)
@@ -119,7 +127,8 @@ def strip_domain(domain):
if isinstance(domain, Mesh):
return Mesh(domain.ufl_coordinate_element(), domain.ufl_id())
elif isinstance(domain, MeshView):
- return MeshView(strip_domain(domain.ufl_mesh()),
- domain.topological_dimension(), domain.ufl_id())
+ return MeshView(
+ strip_domain(domain.ufl_mesh()), domain.topological_dimension(), domain.ufl_id()
+ )
else:
raise NotImplementedError(f"{type(domain)} cannot be stripped")
diff --git a/ufl/algorithms/transformer.py b/ufl/algorithms/transformer.py
index e6eed85c3..6a3328c8a 100644
--- a/ufl/algorithms/transformer.py
+++ b/ufl/algorithms/transformer.py
@@ -34,6 +34,7 @@ class Transformer(object):
Base class for a visitor-like algorithm design pattern used to
transform expression trees from one representation to another.
"""
+
_handlers_cache = {}
def __init__(self, variable_cache=None):
@@ -64,17 +65,16 @@ def __init__(self, variable_cache=None):
handler_name = UFLType._ufl_handler_name_
function = getattr(self, handler_name, None)
if function:
- cache_data[
- classobject.
- _ufl_typecode_] = handler_name, is_post_handler(
- function)
+ cache_data[classobject._ufl_typecode_] = (
+ handler_name,
+ is_post_handler(function),
+ )
break
Transformer._handlers_cache[type(self)] = cache_data
# Build handler list for this particular class (get functions
# bound to self)
- self._handlers = [(getattr(self, name), post)
- for (name, post) in cache_data]
+ self._handlers = [(getattr(self, name), post) for (name, post) in cache_data]
# Keep a stack of objects visit is called on, to ease
# backtracking
self._visit_stack = []
@@ -236,9 +236,12 @@ def variable(self, o):
def apply_transformer(e, transformer, integral_type=None):
- """Apply transformer.visit(expression) to each integrand expression in form, or to form if it is an Expr."""
- return map_integrands(lambda expr: transformer.visit(expr), e,
- integral_type)
+ """Apply transforms.
+
+ Apply transformer.visit(expression) to each integrand expression in
+ form, or to form if it is an Expr.
+ """
+ return map_integrands(lambda expr: transformer.visit(expr), e, integral_type)
def strip_variables(e):
diff --git a/ufl/algorithms/traversal.py b/ufl/algorithms/traversal.py
index 78a0f1b2f..5f6688086 100644
--- a/ufl/algorithms/traversal.py
+++ b/ufl/algorithms/traversal.py
@@ -16,7 +16,7 @@
def iter_expressions(a):
- """Utility function to handle Form, Integral and any Expr the same way when inspecting expressions.
+ """Handle Form, Integral and any Expr the same way when inspecting expressions.
Returns an iterable over Expr instances:
- a is an Expr: (a,)
diff --git a/ufl/argument.py b/ufl/argument.py
index 81b94b9ec..a9326a237 100644
--- a/ufl/argument.py
+++ b/ufl/argument.py
@@ -29,6 +29,7 @@
# --- Class representing an argument (basis function) in a form ---
+
class BaseArgument(object):
"""UFL value: Representation of an argument to a form."""
@@ -40,12 +41,12 @@ def __getnewargs__(self):
return (self._ufl_function_space, self._number, self._part)
def __init__(self, function_space, number, part=None):
- """initialise."""
+ """Initialise."""
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.value_shape
if not isinstance(number, numbers.Integral):
raise ValueError(f"Expecting an int for number, not {number}")
@@ -94,7 +95,11 @@ def ufl_domains(self):
return self._ufl_function_space.ufl_domains()
def _ufl_signature_data_(self, renumbering):
- """Signature data for form arguments depend on the global numbering of the form arguments and domains."""
+ """Signature data.
+
+ Signature data for form arguments depend on the global numbering
+ of the form arguments and domains.
+ """
fsdata = self._ufl_function_space._ufl_signature_data_(renumbering)
return ("Argument", self._number, self._part, fsdata)
@@ -132,8 +137,10 @@ def __eq__(self, other):
are the same ufl element but different dolfin function spaces.
"""
return (
- type(self) is type(other) and self._number == other._number and # noqa: W504
- self._part == other._part and self._ufl_function_space == other._ufl_function_space
+ type(self) is type(other)
+ and self._number == other._number
+ and self._part == other._part
+ and self._ufl_function_space == other._ufl_function_space
)
@@ -169,7 +176,10 @@ def __init__(self, function_space, number, part=None):
BaseArgument.__init__(self, function_space, number, part)
self._repr = "Argument(%s, %s, %s)" % (
- repr(self._ufl_function_space), repr(self._number), repr(self._part))
+ repr(self._ufl_function_space),
+ repr(self._number),
+ repr(self._part),
+ )
def ufl_domains(self):
"""Return UFL domains."""
@@ -193,7 +203,7 @@ class Coargument(BaseForm, BaseArgument):
"_number",
"_part",
"_repr",
- "_hash"
+ "_hash",
)
_primal = False
@@ -202,8 +212,10 @@ class Coargument(BaseForm, BaseArgument):
def __new__(cls, *args, **kw):
"""Create a new Coargument."""
if args[0] and is_primal(args[0]):
- raise ValueError("ufl.Coargument takes in a dual space! If you want to define an argument "
- "in the primal space you should use ufl.Argument.")
+ raise ValueError(
+ "ufl.Coargument takes in a dual space! If you want to define an argument "
+ "in the primal space you should use ufl.Argument."
+ )
return super().__new__(cls)
def __init__(self, function_space, number, part=None):
@@ -214,7 +226,10 @@ def __init__(self, function_space, number, part=None):
self.ufl_operands = ()
self._hash = None
self._repr = "Coargument(%s, %s, %s)" % (
- repr(self._ufl_function_space), repr(self._number), repr(self._part))
+ repr(self._ufl_function_space),
+ repr(self._number),
+ repr(self._part),
+ )
def arguments(self, outer_form=None):
"""Return all Argument objects found in form."""
@@ -228,8 +243,9 @@ def _analyze_form_arguments(self, outer_form=None):
self._coefficients = ()
# Coarguments map from V* to V*, i.e. V* -> V*, or equivalently V* x V -> R.
# So they have one argument in the primal space and one in the dual space.
- # However, when they are composed with linear forms with dual arguments, such as BaseFormOperators,
- # the primal argument is discarded when analysing the argument as Coarguments.
+ # However, when they are composed with linear forms with dual
+ # arguments, such as BaseFormOperators, the primal argument is
+ # discarded when analysing the argument as Coarguments.
if not outer_form:
self._arguments = (Argument(self.ufl_function_space().dual(), 0), self)
else:
@@ -245,15 +261,16 @@ def equals(self, other):
return False
if self is other:
return True
- return (self._ufl_function_space == other._ufl_function_space and # noqa: W504
- self._number == other._number and self._part == other._part)
+ return (
+ self._ufl_function_space == other._ufl_function_space
+ and self._number == other._number
+ and self._part == other._part
+ )
def __hash__(self):
"""Hash."""
- return hash(("Coargument",
- hash(self._ufl_function_space),
- self._number,
- self._part))
+ return hash(("Coargument", hash(self._ufl_function_space), self._number, self._part))
+
# --- Helper functions for pretty syntax ---
@@ -270,14 +287,17 @@ def TrialFunction(function_space, part=None):
# --- Helper functions for creating subfunctions on mixed elements ---
+
def Arguments(function_space, number):
"""Create an Argument in a mixed space.
Returns a tuple with the function components corresponding to the subelements.
"""
if isinstance(function_space, MixedFunctionSpace):
- return [Argument(function_space.ufl_sub_space(i), number, i)
- for i in range(function_space.num_sub_spaces())]
+ return [
+ Argument(function_space.ufl_sub_space(i), number, i)
+ for i in range(function_space.num_sub_spaces())
+ ]
else:
return split(Argument(function_space, number))
@@ -285,7 +305,8 @@ def Arguments(function_space, number):
def TestFunctions(function_space):
"""Create a TestFunction in a mixed space.
- Returns a tuple with the function components corresponding to the subelements.
+ Returns a tuple with the function components corresponding to the
+ subelements.
"""
return Arguments(function_space, 0)
@@ -293,6 +314,7 @@ def TestFunctions(function_space):
def TrialFunctions(function_space):
"""Create a TrialFunction in a mixed space.
- Returns a tuple with the function components corresponding to the subelements.
+ Returns a tuple with the function components corresponding to the
+ subelements.
"""
return Arguments(function_space, 1)
diff --git a/ufl/averaging.py b/ufl/averaging.py
index 899cc3ca8..5c04c8912 100644
--- a/ufl/averaging.py
+++ b/ufl/averaging.py
@@ -11,10 +11,9 @@
from ufl.core.ufl_type import ufl_type
-@ufl_type(inherit_shape_from_operand=0,
- inherit_indices_from_operand=0,
- num_ops=1,
- is_evaluation=True)
+@ufl_type(
+ inherit_shape_from_operand=0, inherit_indices_from_operand=0, num_ops=1, is_evaluation=True
+)
class CellAvg(Operator):
"""Cell average."""
@@ -37,18 +36,16 @@ def ufl_shape(self):
def evaluate(self, x, mapping, component, index_values):
"""Performs an approximate symbolic evaluation, since we don't have a cell."""
- return self.ufl_operands[0].evaluate(x, mapping, component,
- index_values)
+ return self.ufl_operands[0].evaluate(x, mapping, component, index_values)
def __str__(self):
"""Format as a string."""
return f"cell_avg({self.ufl_operands[0]})"
-@ufl_type(inherit_shape_from_operand=0,
- inherit_indices_from_operand=0,
- num_ops=1,
- is_evaluation=True)
+@ufl_type(
+ inherit_shape_from_operand=0, inherit_indices_from_operand=0, num_ops=1, is_evaluation=True
+)
class FacetAvg(Operator):
"""Facet average."""
diff --git a/ufl/cell.py b/ufl/cell.py
index e7df446fe..d85c305e5 100644
--- a/ufl/cell.py
+++ b/ufl/cell.py
@@ -26,10 +26,6 @@ class AbstractCell(UFLObject):
def topological_dimension(self) -> int:
"""Return the dimension of the topology of this cell."""
- @abstractmethod
- def geometric_dimension(self) -> int:
- """Return the dimension of the geometry of this cell."""
-
@abstractmethod
def is_simplex(self) -> bool:
"""Return True if this is a simplex cell."""
@@ -40,7 +36,11 @@ def has_simplex_facets(self) -> bool:
@abstractmethod
def _lt(self, other) -> bool:
- """Define an arbitrarily chosen but fixed sort order for all instances of this type with the same dimensions."""
+ """Less than operator.
+
+ Define an arbitrarily chosen but fixed sort order for all
+ instances of this type with the same dimensions.
+ """
@abstractmethod
def num_sub_entities(self, dim: int) -> int:
@@ -65,8 +65,8 @@ def reconstruct(self, **kwargs: typing.Any) -> Cell:
def __lt__(self, other: AbstractCell) -> bool:
"""Define an arbitrarily chosen but fixed sort order for all cells."""
if type(self) is type(other):
- s = (self.geometric_dimension(), self.topological_dimension())
- o = (other.geometric_dimension(), other.topological_dimension())
+ s = self.topological_dimension()
+ o = other.topological_dimension()
if s != o:
return s < o
return self._lt(other)
@@ -185,36 +185,76 @@ def peak_types(self) -> typing.Tuple[AbstractCell, ...]:
_sub_entity_celltypes = {
- "vertex": [("vertex", )],
- "interval": [tuple("vertex" for i in range(2)), ("interval", )],
- "triangle": [tuple("vertex" for i in range(3)), tuple("interval" for i in range(3)), ("triangle", )],
- "quadrilateral": [tuple("vertex" for i in range(4)), tuple("interval" for i in range(4)), ("quadrilateral", )],
- "tetrahedron": [tuple("vertex" for i in range(4)), tuple("interval" for i in range(6)),
- tuple("triangle" for i in range(4)), ("tetrahedron", )],
- "hexahedron": [tuple("vertex" for i in range(8)), tuple("interval" for i in range(12)),
- tuple("quadrilateral" for i in range(6)), ("hexahedron", )],
- "prism": [tuple("vertex" for i in range(6)), tuple("interval" for i in range(9)),
- ("triangle", "quadrilateral", "quadrilateral", "quadrilateral", "triangle"), ("prism", )],
- "pyramid": [tuple("vertex" for i in range(5)), tuple("interval" for i in range(8)),
- ("quadrilateral", "triangle", "triangle", "triangle", "triangle"), ("pyramid", )],
- "pentatope": [tuple("vertex" for i in range(5)), tuple("interval" for i in range(10)),
- tuple("triangle" for i in range(10)), tuple("tetrahedron" for i in range(5)), ("pentatope", )],
- "tesseract": [tuple("vertex" for i in range(16)), tuple("interval" for i in range(32)),
- tuple("quadrilateral" for i in range(24)), tuple("hexahedron" for i in range(8)), ("tesseract", )],
+ "vertex": [("vertex",)],
+ "interval": [tuple("vertex" for i in range(2)), ("interval",)],
+ "triangle": [
+ tuple("vertex" for i in range(3)),
+ tuple("interval" for i in range(3)),
+ ("triangle",),
+ ],
+ "quadrilateral": [
+ tuple("vertex" for i in range(4)),
+ tuple("interval" for i in range(4)),
+ ("quadrilateral",),
+ ],
+ "tetrahedron": [
+ tuple("vertex" for i in range(4)),
+ tuple("interval" for i in range(6)),
+ tuple("triangle" for i in range(4)),
+ ("tetrahedron",),
+ ],
+ "hexahedron": [
+ tuple("vertex" for i in range(8)),
+ tuple("interval" for i in range(12)),
+ tuple("quadrilateral" for i in range(6)),
+ ("hexahedron",),
+ ],
+ "prism": [
+ tuple("vertex" for i in range(6)),
+ tuple("interval" for i in range(9)),
+ ("triangle", "quadrilateral", "quadrilateral", "quadrilateral", "triangle"),
+ ("prism",),
+ ],
+ "pyramid": [
+ tuple("vertex" for i in range(5)),
+ tuple("interval" for i in range(8)),
+ ("quadrilateral", "triangle", "triangle", "triangle", "triangle"),
+ ("pyramid",),
+ ],
+ "pentatope": [
+ tuple("vertex" for i in range(5)),
+ tuple("interval" for i in range(10)),
+ tuple("triangle" for i in range(10)),
+ tuple("tetrahedron" for i in range(5)),
+ ("pentatope",),
+ ],
+ "tesseract": [
+ tuple("vertex" for i in range(16)),
+ tuple("interval" for i in range(32)),
+ tuple("quadrilateral" for i in range(24)),
+ tuple("hexahedron" for i in range(8)),
+ ("tesseract",),
+ ],
}
class Cell(AbstractCell):
"""Representation of a named finite element cell with known structure."""
- __slots__ = ("_cellname", "_tdim", "_gdim", "_num_cell_entities", "_sub_entity_types",
- "_sub_entities", "_sub_entity_types")
- def __init__(self, cellname: str, geometric_dimension: typing.Optional[int] = None):
+ __slots__ = (
+ "_cellname",
+ "_tdim",
+ "_num_cell_entities",
+ "_sub_entity_types",
+ "_sub_entities",
+ "_sub_entity_types",
+ )
+
+ def __init__(self, cellname: str):
"""Initialise.
Args:
cellname: Name of the cell
- geometric_dimension: Geometric dimension
"""
if cellname not in _sub_entity_celltypes:
raise ValueError(f"Unsupported cell type: {cellname}")
@@ -223,30 +263,22 @@ def __init__(self, cellname: str, geometric_dimension: typing.Optional[int] = No
self._cellname = cellname
self._tdim = len(self._sub_entity_celltypes) - 1
- self._gdim = self._tdim if geometric_dimension is None else geometric_dimension
self._num_cell_entities = [len(i) for i in self._sub_entity_celltypes]
- self._sub_entities = [tuple(Cell(t, self._gdim) for t in se_types)
- for se_types in self._sub_entity_celltypes[:-1]]
+ self._sub_entities = [
+ tuple(Cell(t) for t in se_types) for se_types in self._sub_entity_celltypes[:-1]
+ ]
self._sub_entity_types = [tuple(set(i)) for i in self._sub_entities]
- self._sub_entities.append((weakref.proxy(self), ))
- self._sub_entity_types.append((weakref.proxy(self), ))
+ self._sub_entities.append((weakref.proxy(self),))
+ self._sub_entity_types.append((weakref.proxy(self),))
- if not isinstance(self._gdim, numbers.Integral):
- raise ValueError("Expecting integer geometric_dimension.")
if not isinstance(self._tdim, numbers.Integral):
raise ValueError("Expecting integer topological_dimension.")
- if self._tdim > self._gdim:
- raise ValueError("Topological dimension cannot be larger than geometric dimension.")
def topological_dimension(self) -> int:
"""Return the dimension of the topology of this cell."""
return self._tdim
- def geometric_dimension(self) -> int:
- """Return the dimension of the geometry of this cell."""
- return self._gdim
-
def is_simplex(self) -> bool:
"""Return True if this is a simplex cell."""
return self._cellname in ["vertex", "interval", "triangle", "tetrahedron"]
@@ -291,56 +323,40 @@ def cellname(self) -> str:
def __str__(self) -> str:
"""Format as a string."""
- if self._gdim == self._tdim:
- return self._cellname
- else:
- return f"{self._cellname}{self._gdim}D"
+ return self._cellname
def __repr__(self) -> str:
"""Representation."""
- if self._gdim == self._tdim:
- return self._cellname
- else:
- return f"Cell({self._cellname}, {self._gdim})"
+ return self._cellname
def _ufl_hash_data_(self) -> typing.Hashable:
"""UFL hash data."""
- return (self._cellname, self._gdim)
+ return (self._cellname,)
def reconstruct(self, **kwargs: typing.Any) -> Cell:
"""Reconstruct this cell, overwriting properties by those in kwargs."""
- gdim = self._gdim
for key, value in kwargs.items():
- if key == "geometric_dimension":
- gdim = value
- else:
- raise TypeError(f"reconstruct() got unexpected keyword argument '{key}'")
- return Cell(self._cellname, geometric_dimension=gdim)
+ raise TypeError(f"reconstruct() got unexpected keyword argument '{key}'")
+ return Cell(self._cellname)
class TensorProductCell(AbstractCell):
"""Tensor product cell."""
- __slots__ = ("_cells", "_tdim", "_gdim")
+ __slots__ = ("_cells", "_tdim")
- def __init__(self, *cells: Cell, geometric_dimension: typing.Optional[int] = None):
+ def __init__(self, *cells: Cell):
"""Initialise.
Args:
cells: Cells to take the tensor product of
- geometric_dimension: Geometric dimension
"""
self._cells = tuple(as_cell(cell) for cell in cells)
self._tdim = sum([cell.topological_dimension() for cell in self._cells])
- self._gdim = self._tdim if geometric_dimension is None else geometric_dimension
- if not isinstance(self._gdim, numbers.Integral):
- raise ValueError("Expecting integer geometric_dimension.")
if not isinstance(self._tdim, numbers.Integral):
raise ValueError("Expecting integer topological_dimension.")
- if self._tdim > self._gdim:
- raise ValueError("Topological dimension cannot be larger than geometric dimension.")
def sub_cells(self) -> typing.List[AbstractCell]:
"""Return list of cell factors."""
@@ -350,10 +366,6 @@ def topological_dimension(self) -> int:
"""Return the dimension of the topology of this cell."""
return self._tdim
- def geometric_dimension(self) -> int:
- """Return the dimension of the geometry of this cell."""
- return self._gdim
-
def is_simplex(self) -> bool:
"""Return True if this is a simplex cell."""
if len(self._cells) == 1:
@@ -375,8 +387,9 @@ def num_sub_entities(self, dim: int) -> int:
if dim == 0:
return functools.reduce(lambda x, y: x * y, [c.num_vertices() for c in self._cells])
if dim == self._tdim - 1:
- # Note: This is not the number of facets that the cell has, but I'm leaving it here for now
- # to not change past behaviour
+ # Note: This is not the number of facets that the cell has,
+ # but I'm leaving it here for now to not change past
+ # behaviour
return sum(c.num_facets() for c in self._cells if c.topological_dimension() > 0)
if dim == self._tdim:
return 1
@@ -387,7 +400,7 @@ def sub_entities(self, dim: int) -> typing.Tuple[AbstractCell, ...]:
if dim < 0 or dim > self._tdim:
return []
if dim == 0:
- return [Cell("vertex", self._gdim) for i in range(self.num_sub_entities(0))]
+ return [Cell("vertex") for i in range(self.num_sub_entities(0))]
if dim == self._tdim:
return [self]
raise NotImplementedError(f"TensorProductCell.sub_entities({dim}) is not implemented.")
@@ -397,7 +410,7 @@ def sub_entity_types(self, dim: int) -> typing.Tuple[AbstractCell, ...]:
if dim < 0 or dim > self._tdim:
return []
if dim == 0:
- return [Cell("vertex", self._gdim)]
+ return [Cell("vertex")]
if dim == self._tdim:
return [self]
raise NotImplementedError(f"TensorProductCell.sub_entities({dim}) is not implemented.")
@@ -411,12 +424,7 @@ def cellname(self) -> str:
def __str__(self) -> str:
"""Format as a string."""
- s = "TensorProductCell("
- s += ", ".join(f"{c!r}" for c in self._cells)
- if self._tdim != self._gdim:
- s += f", geometric_dimension={self._gdim}"
- s += ")"
- return s
+ return "TensorProductCell(" + ", ".join(f"{c!r}" for c in self._cells) + ")"
def __repr__(self) -> str:
"""Representation."""
@@ -424,46 +432,42 @@ def __repr__(self) -> str:
def _ufl_hash_data_(self) -> typing.Hashable:
"""UFL hash data."""
- return tuple(c._ufl_hash_data_() for c in self._cells) + (self._gdim,)
+ return tuple(c._ufl_hash_data_() for c in self._cells)
def reconstruct(self, **kwargs: typing.Any) -> Cell:
"""Reconstruct this cell, overwriting properties by those in kwargs."""
- gdim = self._gdim
for key, value in kwargs.items():
- if key == "geometric_dimension":
- gdim = value
- else:
- raise TypeError(f"reconstruct() got unexpected keyword argument '{key}'")
- return TensorProductCell(*self._cells, geometric_dimension=gdim)
+ raise TypeError(f"reconstruct() got unexpected keyword argument '{key}'")
+ return TensorProductCell(*self._cells)
-def simplex(topological_dimension: int, geometric_dimension: typing.Optional[int] = None):
+def simplex(topological_dimension: int):
"""Return a simplex cell of the given dimension."""
if topological_dimension == 0:
- return Cell("vertex", geometric_dimension)
+ return Cell("vertex")
if topological_dimension == 1:
- return Cell("interval", geometric_dimension)
+ return Cell("interval")
if topological_dimension == 2:
- return Cell("triangle", geometric_dimension)
+ return Cell("triangle")
if topological_dimension == 3:
- return Cell("tetrahedron", geometric_dimension)
+ return Cell("tetrahedron")
if topological_dimension == 4:
- return Cell("pentatope", geometric_dimension)
+ return Cell("pentatope")
raise ValueError(f"Unsupported topological dimension for simplex: {topological_dimension}")
-def hypercube(topological_dimension, geometric_dimension=None):
+def hypercube(topological_dimension: int):
"""Return a hypercube cell of the given dimension."""
if topological_dimension == 0:
- return Cell("vertex", geometric_dimension)
+ return Cell("vertex")
if topological_dimension == 1:
- return Cell("interval", geometric_dimension)
+ return Cell("interval")
if topological_dimension == 2:
- return Cell("quadrilateral", geometric_dimension)
+ return Cell("quadrilateral")
if topological_dimension == 3:
- return Cell("hexahedron", geometric_dimension)
+ return Cell("hexahedron")
if topological_dimension == 4:
- return Cell("tesseract", geometric_dimension)
+ return Cell("tesseract")
raise ValueError(f"Unsupported topological dimension for hypercube: {topological_dimension}")
diff --git a/ufl/checks.py b/ufl/checks.py
index 5b4dc8ce1..09ae4453b 100644
--- a/ufl/checks.py
+++ b/ufl/checks.py
@@ -27,7 +27,9 @@ def is_ufl_scalar(expression):
def is_true_ufl_scalar(expression):
"""Return True iff expression is scalar-valued, with no free indices."""
- return isinstance(expression, Expr) and not (expression.ufl_shape or expression.ufl_free_indices)
+ return isinstance(expression, Expr) and not (
+ expression.ufl_shape or expression.ufl_free_indices
+ )
def is_cellwise_constant(expr):
diff --git a/ufl/classes.py b/ufl/classes.py
index b65c802d4..b8f48516c 100644
--- a/ufl/classes.py
+++ b/ufl/classes.py
@@ -88,6 +88,7 @@ def populate_namespace_with_expr_classes(namespace):
# Semi-automated imports of non-expr classes:
+
def populate_namespace_with_module_classes(mod, loc):
"""Export the classes that submodules list in __all_classes__."""
names = mod.__all_classes__
diff --git a/ufl/coefficient.py b/ufl/coefficient.py
index 634d3bc49..bd35f8275 100644
--- a/ufl/coefficient.py
+++ b/ufl/coefficient.py
@@ -45,10 +45,9 @@ def __init__(self, function_space, count=None):
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.value_shape
- self._repr = "BaseCoefficient(%s, %s)" % (
- repr(self._ufl_function_space), repr(self._count))
+ self._repr = "BaseCoefficient(%s, %s)" % (repr(self._ufl_function_space), repr(self._count))
@property
def ufl_shape(self):
@@ -76,7 +75,11 @@ def ufl_domains(self):
return self._ufl_function_space.ufl_domains()
def _ufl_signature_data_(self, renumbering):
- """Signature data for form arguments depend on the global numbering of the form arguments and domains."""
+ """Signature data.
+
+ Signature data for form arguments depend on the global numbering
+ of the form arguments and domains.
+ """
count = renumbering[self]
fsdata = self._ufl_function_space._ufl_signature_data_(renumbering)
return ("Coefficient", count, fsdata)
@@ -111,7 +114,7 @@ class Cofunction(BaseCoefficient, BaseForm):
"ufl_operands",
"_repr",
"_ufl_shape",
- "_hash"
+ "_hash",
)
_primal = False
_dual = True
@@ -122,8 +125,10 @@ class Cofunction(BaseCoefficient, BaseForm):
def __new__(cls, *args, **kw):
"""Create a new Cofunction."""
if args[0] and is_primal(args[0]):
- raise ValueError("ufl.Cofunction takes in a dual space. If you want to define a coefficient "
- "in the primal space you should use ufl.Coefficient.")
+ raise ValueError(
+ "ufl.Cofunction takes in a dual space. If you want to define a coefficient "
+ "in the primal space you should use ufl.Coefficient."
+ )
return super().__new__(cls)
def __init__(self, function_space, count=None):
@@ -133,8 +138,7 @@ def __init__(self, function_space, count=None):
self.ufl_operands = ()
self._hash = None
- self._repr = "Cofunction(%s, %s)" % (
- repr(self._ufl_function_space), repr(self._count))
+ self._repr = "Cofunction(%s, %s)" % (repr(self._ufl_function_space), repr(self._count))
def equals(self, other):
"""Check equality."""
@@ -146,9 +150,7 @@ def equals(self, other):
def __hash__(self):
"""Hash."""
- return hash(("Cofunction",
- hash(self._ufl_function_space),
- self._count))
+ return hash(("Cofunction", hash(self._ufl_function_space), self._count))
def _analyze_form_arguments(self):
"""Analyze which Argument and Coefficient objects can be found in the form."""
@@ -181,8 +183,7 @@ def __init__(self, function_space, count=None):
FormArgument.__init__(self)
BaseCoefficient.__init__(self, function_space, count)
- self._repr = "Coefficient(%s, %s)" % (
- repr(self._ufl_function_space), repr(self._count))
+ self._repr = "Coefficient(%s, %s)" % (repr(self._ufl_function_space), repr(self._count))
def ufl_domains(self):
"""Get the UFL domains."""
@@ -203,13 +204,16 @@ def __repr__(self):
# --- Helper functions for subfunctions on mixed elements ---
+
def Coefficients(function_space):
"""Create a Coefficient in a mixed space.
Returns a tuple with the function components corresponding to the subelements.
"""
if isinstance(function_space, MixedFunctionSpace):
- return [Coefficient(fs) if is_primal(fs) else Cofunction(fs)
- for fs in function_space.num_sub_spaces()]
+ return [
+ Coefficient(fs) if is_primal(fs) else Cofunction(fs)
+ for fs in function_space.num_sub_spaces()
+ ]
else:
return split(Coefficient(function_space))
diff --git a/ufl/compound_expressions.py b/ufl/compound_expressions.py
index 6b9c63402..6f92dd00d 100644
--- a/ufl/compound_expressions.py
+++ b/ufl/compound_expressions.py
@@ -1,4 +1,4 @@
-"""Functions implementing compound expressions as equivalent representations using basic operators."""
+"""Support for compound expressions as equivalent representations using basic operators."""
# Copyright (C) 2008-2016 Martin Sandve Alnæs and Anders Logg
#
# This file is part of UFL (https://www.fenicsproject.org)
@@ -7,8 +7,6 @@
#
# Modified by Anders Logg, 2009-2010
-import warnings
-
from ufl.constantvalue import Zero, zero
from ufl.core.multiindex import Index, indices
from ufl.operators import sqrt
@@ -31,6 +29,7 @@ def cross_expr(a, b):
def c(i, j):
return a[i] * b[j] - a[j] * b[i]
+
return as_vector((c(1, 2), c(2, 0), c(0, 1)))
@@ -112,13 +111,6 @@ def determinant_expr_2x2(B):
return _det_2x2(B, 0, 1, 0, 1)
-def old_determinant_expr_3x3(A):
- """Determinant of a 3 by 3 matrix."""
- warnings.warn("The use of old_determinant_expr_3x3 is deprecated and will be removed after December 2023. "
- "Please, use determinant_expr_3x3 instead", FutureWarning)
- return A[0, 0] * _det_2x2(A, 1, 2, 1, 2) + A[0, 1] * _det_2x2(A, 1, 2, 2, 0) + A[0, 2] * _det_2x2(A, 1, 2, 0, 1)
-
-
def determinant_expr_3x3(A):
"""Determinant of a 3 by 3 matrix."""
return codeterminant_expr_nxn(A, [0, 1, 2], [0, 1, 2])
@@ -139,8 +131,8 @@ def codeterminant_expr_nxn(A, rows, cols):
r = rows[0]
subrows = rows[1:]
for i, c in enumerate(cols):
- subcols = cols[:i] + cols[i + 1:]
- codet += (-1)**i * A[r, c] * codeterminant_expr_nxn(A, subrows, subcols)
+ subcols = cols[:i] + cols[i + 1 :]
+ codet += (-1) ** i * A[r, c] * codeterminant_expr_nxn(A, subrows, subcols)
return codet
@@ -176,50 +168,142 @@ def adj_expr(A):
def adj_expr_2x2(A):
"""Adjoint of a 2 by 2 matrix."""
- return as_matrix([[A[1, 1], -A[0, 1]],
- [-A[1, 0], A[0, 0]]])
+ return as_matrix([[A[1, 1], -A[0, 1]], [-A[1, 0], A[0, 0]]])
def adj_expr_3x3(A):
"""Adjoint of a 3 by 3 matrix."""
- return as_matrix([[
- A[2, 2] * A[1, 1] - A[1, 2] * A[2, 1],
- -A[0, 1] * A[2, 2] + A[0, 2] * A[2, 1],
- A[0, 1] * A[1, 2] - A[0, 2] * A[1, 1],
- ], [
- -A[2, 2] * A[1, 0] + A[1, 2] * A[2, 0],
- -A[0, 2] * A[2, 0] + A[2, 2] * A[0, 0],
- A[0, 2] * A[1, 0] - A[1, 2] * A[0, 0],
- ], [
- A[1, 0] * A[2, 1] - A[2, 0] * A[1, 1],
- A[0, 1] * A[2, 0] - A[0, 0] * A[2, 1],
- A[0, 0] * A[1, 1] - A[0, 1] * A[1, 0],
- ]])
+ return as_matrix(
+ [
+ [
+ A[2, 2] * A[1, 1] - A[1, 2] * A[2, 1],
+ -A[0, 1] * A[2, 2] + A[0, 2] * A[2, 1],
+ A[0, 1] * A[1, 2] - A[0, 2] * A[1, 1],
+ ],
+ [
+ -A[2, 2] * A[1, 0] + A[1, 2] * A[2, 0],
+ -A[0, 2] * A[2, 0] + A[2, 2] * A[0, 0],
+ A[0, 2] * A[1, 0] - A[1, 2] * A[0, 0],
+ ],
+ [
+ A[1, 0] * A[2, 1] - A[2, 0] * A[1, 1],
+ A[0, 1] * A[2, 0] - A[0, 0] * A[2, 1],
+ A[0, 0] * A[1, 1] - A[0, 1] * A[1, 0],
+ ],
+ ]
+ )
def adj_expr_4x4(A):
"""Adjoint of a 4 by 4 matrix."""
- return as_matrix([[
- -A[3, 3] * A[2, 1] * A[1, 2] + A[1, 2] * A[3, 1] * A[2, 3] + A[1, 1] * A[3, 3] * A[2, 2] - A[3, 1] * A[2, 2] * A[1, 3] + A[2, 1] * A[1, 3] * A[3, 2] - A[1, 1] * A[3, 2] * A[2, 3], # noqa: E501
- -A[3, 1] * A[0, 2] * A[2, 3] + A[0, 1] * A[3, 2] * A[2, 3] - A[0, 3] * A[2, 1] * A[3, 2] + A[3, 3] * A[2, 1] * A[0, 2] - A[3, 3] * A[0, 1] * A[2, 2] + A[0, 3] * A[3, 1] * A[2, 2], # noqa: E501
- A[3, 1] * A[1, 3] * A[0, 2] + A[1, 1] * A[0, 3] * A[3, 2] - A[0, 3] * A[1, 2] * A[3, 1] - A[0, 1] * A[1, 3] * A[3, 2] + A[3, 3] * A[1, 2] * A[0, 1] - A[1, 1] * A[3, 3] * A[0, 2], # noqa: E501
- A[1, 1] * A[0, 2] * A[2, 3] - A[2, 1] * A[1, 3] * A[0, 2] + A[0, 3] * A[2, 1] * A[1, 2] - A[1, 2] * A[0, 1] * A[2, 3] - A[1, 1] * A[0, 3] * A[2, 2] + A[0, 1] * A[2, 2] * A[1, 3], # noqa: E501
- ], [
- A[3, 3] * A[1, 2] * A[2, 0] - A[3, 0] * A[1, 2] * A[2, 3] + A[1, 0] * A[3, 2] * A[2, 3] - A[3, 3] * A[1, 0] * A[2, 2] - A[1, 3] * A[3, 2] * A[2, 0] + A[3, 0] * A[2, 2] * A[1, 3], # noqa: E501
- A[0, 3] * A[3, 2] * A[2, 0] - A[0, 3] * A[3, 0] * A[2, 2] + A[3, 3] * A[0, 0] * A[2, 2] + A[3, 0] * A[0, 2] * A[2, 3] - A[0, 0] * A[3, 2] * A[2, 3] - A[3, 3] * A[0, 2] * A[2, 0], # noqa: E501
- -A[3, 3] * A[0, 0] * A[1, 2] + A[0, 0] * A[1, 3] * A[3, 2] - A[3, 0] * A[1, 3] * A[0, 2] + A[3, 3] * A[1, 0] * A[0, 2] + A[0, 3] * A[3, 0] * A[1, 2] - A[0, 3] * A[1, 0] * A[3, 2], # noqa: E501
- A[0, 3] * A[1, 0] * A[2, 2] + A[1, 3] * A[0, 2] * A[2, 0] - A[0, 0] * A[2, 2] * A[1, 3] - A[0, 3] * A[1, 2] * A[2, 0] + A[0, 0] * A[1, 2] * A[2, 3] - A[1, 0] * A[0, 2] * A[2, 3], # noqa: E501
- ], [
- A[3, 1] * A[1, 3] * A[2, 0] + A[3, 3] * A[2, 1] * A[1, 0] + A[1, 1] * A[3, 0] * A[2, 3] - A[1, 0] * A[3, 1] * A[2, 3] - A[3, 0] * A[2, 1] * A[1, 3] - A[1, 1] * A[3, 3] * A[2, 0], # noqa: E501
- A[3, 3] * A[0, 1] * A[2, 0] - A[3, 3] * A[0, 0] * A[2, 1] - A[0, 3] * A[3, 1] * A[2, 0] - A[3, 0] * A[0, 1] * A[2, 3] + A[0, 0] * A[3, 1] * A[2, 3] + A[0, 3] * A[3, 0] * A[2, 1], # noqa: E501
- -A[0, 0] * A[3, 1] * A[1, 3] + A[0, 3] * A[1, 0] * A[3, 1] - A[3, 3] * A[1, 0] * A[0, 1] + A[1, 1] * A[3, 3] * A[0, 0] - A[1, 1] * A[0, 3] * A[3, 0] + A[3, 0] * A[0, 1] * A[1, 3], # noqa: E501
- A[0, 0] * A[2, 1] * A[1, 3] + A[1, 0] * A[0, 1] * A[2, 3] - A[0, 3] * A[2, 1] * A[1, 0] + A[1, 1] * A[0, 3] * A[2, 0] - A[1, 1] * A[0, 0] * A[2, 3] - A[0, 1] * A[1, 3] * A[2, 0], # noqa: E501
- ], [
- -A[1, 2] * A[3, 1] * A[2, 0] - A[2, 1] * A[1, 0] * A[3, 2] + A[3, 0] * A[2, 1] * A[1, 2] - A[1, 1] * A[3, 0] * A[2, 2] + A[1, 0] * A[3, 1] * A[2, 2] + A[1, 1] * A[3, 2] * A[2, 0], # noqa: E501
- -A[3, 0] * A[2, 1] * A[0, 2] - A[0, 1] * A[3, 2] * A[2, 0] + A[3, 1] * A[0, 2] * A[2, 0] - A[0, 0] * A[3, 1] * A[2, 2] + A[3, 0] * A[0, 1] * A[2, 2] + A[0, 0] * A[2, 1] * A[3, 2], # noqa: E501
- A[0, 0] * A[1, 2] * A[3, 1] - A[1, 0] * A[3, 1] * A[0, 2] + A[1, 1] * A[3, 0] * A[0, 2] + A[1, 0] * A[0, 1] * A[3, 2] - A[3, 0] * A[1, 2] * A[0, 1] - A[1, 1] * A[0, 0] * A[3, 2], # noqa: E501
- -A[1, 1] * A[0, 2] * A[2, 0] + A[2, 1] * A[1, 0] * A[0, 2] + A[1, 2] * A[0, 1] * A[2, 0] + A[1, 1] * A[0, 0] * A[2, 2] - A[1, 0] * A[0, 1] * A[2, 2] - A[0, 0] * A[2, 1] * A[1, 2], # noqa: E501
- ]])
+ return as_matrix(
+ [
+ [
+ -A[3, 3] * A[2, 1] * A[1, 2]
+ + A[1, 2] * A[3, 1] * A[2, 3]
+ + A[1, 1] * A[3, 3] * A[2, 2]
+ - A[3, 1] * A[2, 2] * A[1, 3]
+ + A[2, 1] * A[1, 3] * A[3, 2]
+ - A[1, 1] * A[3, 2] * A[2, 3],
+ -A[3, 1] * A[0, 2] * A[2, 3]
+ + A[0, 1] * A[3, 2] * A[2, 3]
+ - A[0, 3] * A[2, 1] * A[3, 2]
+ + A[3, 3] * A[2, 1] * A[0, 2]
+ - A[3, 3] * A[0, 1] * A[2, 2]
+ + A[0, 3] * A[3, 1] * A[2, 2],
+ A[3, 1] * A[1, 3] * A[0, 2]
+ + A[1, 1] * A[0, 3] * A[3, 2]
+ - A[0, 3] * A[1, 2] * A[3, 1]
+ - A[0, 1] * A[1, 3] * A[3, 2]
+ + A[3, 3] * A[1, 2] * A[0, 1]
+ - A[1, 1] * A[3, 3] * A[0, 2],
+ A[1, 1] * A[0, 2] * A[2, 3]
+ - A[2, 1] * A[1, 3] * A[0, 2]
+ + A[0, 3] * A[2, 1] * A[1, 2]
+ - A[1, 2] * A[0, 1] * A[2, 3]
+ - A[1, 1] * A[0, 3] * A[2, 2]
+ + A[0, 1] * A[2, 2] * A[1, 3],
+ ],
+ [
+ A[3, 3] * A[1, 2] * A[2, 0]
+ - A[3, 0] * A[1, 2] * A[2, 3]
+ + A[1, 0] * A[3, 2] * A[2, 3]
+ - A[3, 3] * A[1, 0] * A[2, 2]
+ - A[1, 3] * A[3, 2] * A[2, 0]
+ + A[3, 0] * A[2, 2] * A[1, 3],
+ A[0, 3] * A[3, 2] * A[2, 0]
+ - A[0, 3] * A[3, 0] * A[2, 2]
+ + A[3, 3] * A[0, 0] * A[2, 2]
+ + A[3, 0] * A[0, 2] * A[2, 3]
+ - A[0, 0] * A[3, 2] * A[2, 3]
+ - A[3, 3] * A[0, 2] * A[2, 0],
+ -A[3, 3] * A[0, 0] * A[1, 2]
+ + A[0, 0] * A[1, 3] * A[3, 2]
+ - A[3, 0] * A[1, 3] * A[0, 2]
+ + A[3, 3] * A[1, 0] * A[0, 2]
+ + A[0, 3] * A[3, 0] * A[1, 2]
+ - A[0, 3] * A[1, 0] * A[3, 2],
+ A[0, 3] * A[1, 0] * A[2, 2]
+ + A[1, 3] * A[0, 2] * A[2, 0]
+ - A[0, 0] * A[2, 2] * A[1, 3]
+ - A[0, 3] * A[1, 2] * A[2, 0]
+ + A[0, 0] * A[1, 2] * A[2, 3]
+ - A[1, 0] * A[0, 2] * A[2, 3],
+ ],
+ [
+ A[3, 1] * A[1, 3] * A[2, 0]
+ + A[3, 3] * A[2, 1] * A[1, 0]
+ + A[1, 1] * A[3, 0] * A[2, 3]
+ - A[1, 0] * A[3, 1] * A[2, 3]
+ - A[3, 0] * A[2, 1] * A[1, 3]
+ - A[1, 1] * A[3, 3] * A[2, 0],
+ A[3, 3] * A[0, 1] * A[2, 0]
+ - A[3, 3] * A[0, 0] * A[2, 1]
+ - A[0, 3] * A[3, 1] * A[2, 0]
+ - A[3, 0] * A[0, 1] * A[2, 3]
+ + A[0, 0] * A[3, 1] * A[2, 3]
+ + A[0, 3] * A[3, 0] * A[2, 1],
+ -A[0, 0] * A[3, 1] * A[1, 3]
+ + A[0, 3] * A[1, 0] * A[3, 1]
+ - A[3, 3] * A[1, 0] * A[0, 1]
+ + A[1, 1] * A[3, 3] * A[0, 0]
+ - A[1, 1] * A[0, 3] * A[3, 0]
+ + A[3, 0] * A[0, 1] * A[1, 3],
+ A[0, 0] * A[2, 1] * A[1, 3]
+ + A[1, 0] * A[0, 1] * A[2, 3]
+ - A[0, 3] * A[2, 1] * A[1, 0]
+ + A[1, 1] * A[0, 3] * A[2, 0]
+ - A[1, 1] * A[0, 0] * A[2, 3]
+ - A[0, 1] * A[1, 3] * A[2, 0],
+ ],
+ [
+ -A[1, 2] * A[3, 1] * A[2, 0]
+ - A[2, 1] * A[1, 0] * A[3, 2]
+ + A[3, 0] * A[2, 1] * A[1, 2]
+ - A[1, 1] * A[3, 0] * A[2, 2]
+ + A[1, 0] * A[3, 1] * A[2, 2]
+ + A[1, 1] * A[3, 2] * A[2, 0],
+ -A[3, 0] * A[2, 1] * A[0, 2]
+ - A[0, 1] * A[3, 2] * A[2, 0]
+ + A[3, 1] * A[0, 2] * A[2, 0]
+ - A[0, 0] * A[3, 1] * A[2, 2]
+ + A[3, 0] * A[0, 1] * A[2, 2]
+ + A[0, 0] * A[2, 1] * A[3, 2],
+ A[0, 0] * A[1, 2] * A[3, 1]
+ - A[1, 0] * A[3, 1] * A[0, 2]
+ + A[1, 1] * A[3, 0] * A[0, 2]
+ + A[1, 0] * A[0, 1] * A[3, 2]
+ - A[3, 0] * A[1, 2] * A[0, 1]
+ - A[1, 1] * A[0, 0] * A[3, 2],
+ -A[1, 1] * A[0, 2] * A[2, 0]
+ + A[2, 1] * A[1, 0] * A[0, 2]
+ + A[1, 2] * A[0, 1] * A[2, 0]
+ + A[1, 1] * A[0, 0] * A[2, 2]
+ - A[1, 0] * A[0, 1] * A[2, 2]
+ - A[0, 0] * A[2, 1] * A[1, 2],
+ ],
+ ]
+ )
def cofactor_expr(A):
@@ -240,50 +324,142 @@ def cofactor_expr(A):
def cofactor_expr_2x2(A):
"""Cofactor of a 2 by 2 matrix."""
- return as_matrix([[A[1, 1], -A[1, 0]],
- [-A[0, 1], A[0, 0]]])
+ return as_matrix([[A[1, 1], -A[1, 0]], [-A[0, 1], A[0, 0]]])
def cofactor_expr_3x3(A):
"""Cofactor of a 3 by 3 matrix."""
- return as_matrix([[
- A[1, 1] * A[2, 2] - A[2, 1] * A[1, 2],
- A[2, 0] * A[1, 2] - A[1, 0] * A[2, 2],
- -A[2, 0] * A[1, 1] + A[1, 0] * A[2, 1],
- ], [
- A[2, 1] * A[0, 2] - A[0, 1] * A[2, 2],
- A[0, 0] * A[2, 2] - A[2, 0] * A[0, 2],
- -A[0, 0] * A[2, 1] + A[2, 0] * A[0, 1],
- ], [
- A[0, 1] * A[1, 2] - A[1, 1] * A[0, 2],
- A[1, 0] * A[0, 2] - A[0, 0] * A[1, 2],
- -A[1, 0] * A[0, 1] + A[0, 0] * A[1, 1],
- ]])
+ return as_matrix(
+ [
+ [
+ A[1, 1] * A[2, 2] - A[2, 1] * A[1, 2],
+ A[2, 0] * A[1, 2] - A[1, 0] * A[2, 2],
+ -A[2, 0] * A[1, 1] + A[1, 0] * A[2, 1],
+ ],
+ [
+ A[2, 1] * A[0, 2] - A[0, 1] * A[2, 2],
+ A[0, 0] * A[2, 2] - A[2, 0] * A[0, 2],
+ -A[0, 0] * A[2, 1] + A[2, 0] * A[0, 1],
+ ],
+ [
+ A[0, 1] * A[1, 2] - A[1, 1] * A[0, 2],
+ A[1, 0] * A[0, 2] - A[0, 0] * A[1, 2],
+ -A[1, 0] * A[0, 1] + A[0, 0] * A[1, 1],
+ ],
+ ]
+ )
def cofactor_expr_4x4(A):
"""Cofactor of a 4 by 4 matrix."""
- return as_matrix([[
- -A[3, 1] * A[2, 2] * A[1, 3] - A[3, 2] * A[2, 3] * A[1, 1] + A[1, 3] * A[3, 2] * A[2, 1] + A[3, 1] * A[2, 3] * A[1, 2] + A[2, 2] * A[1, 1] * A[3, 3] - A[3, 3] * A[2, 1] * A[1, 2], # noqa: E501
- -A[1, 0] * A[2, 2] * A[3, 3] + A[2, 0] * A[3, 3] * A[1, 2] + A[2, 2] * A[1, 3] * A[3, 0] - A[2, 3] * A[3, 0] * A[1, 2] + A[1, 0] * A[3, 2] * A[2, 3] - A[1, 3] * A[3, 2] * A[2, 0], # noqa: E501
- A[1, 0] * A[3, 3] * A[2, 1] + A[2, 3] * A[1, 1] * A[3, 0] - A[2, 0] * A[1, 1] * A[3, 3] - A[1, 3] * A[3, 0] * A[2, 1] - A[1, 0] * A[3, 1] * A[2, 3] + A[3, 1] * A[1, 3] * A[2, 0], # noqa: E501
- A[3, 0] * A[2, 1] * A[1, 2] + A[1, 0] * A[3, 1] * A[2, 2] + A[3, 2] * A[2, 0] * A[1, 1] - A[2, 2] * A[1, 1] * A[3, 0] - A[3, 1] * A[2, 0] * A[1, 2] - A[1, 0] * A[3, 2] * A[2, 1], # noqa: E501
- ], [
- A[3, 1] * A[2, 2] * A[0, 3] + A[0, 2] * A[3, 3] * A[2, 1] + A[0, 1] * A[3, 2] * A[2, 3] - A[3, 1] * A[0, 2] * A[2, 3] - A[0, 1] * A[2, 2] * A[3, 3] - A[3, 2] * A[0, 3] * A[2, 1], # noqa: E501
- -A[2, 2] * A[0, 3] * A[3, 0] - A[0, 2] * A[2, 0] * A[3, 3] - A[3, 2] * A[2, 3] * A[0, 0] + A[2, 2] * A[3, 3] * A[0, 0] + A[0, 2] * A[2, 3] * A[3, 0] + A[3, 2] * A[2, 0] * A[0, 3], # noqa: E501
- A[3, 1] * A[2, 3] * A[0, 0] - A[0, 1] * A[2, 3] * A[3, 0] - A[3, 1] * A[2, 0] * A[0, 3] - A[3, 3] * A[0, 0] * A[2, 1] + A[0, 3] * A[3, 0] * A[2, 1] + A[0, 1] * A[2, 0] * A[3, 3], # noqa: E501
- A[3, 2] * A[0, 0] * A[2, 1] - A[0, 2] * A[3, 0] * A[2, 1] + A[0, 1] * A[2, 2] * A[3, 0] + A[3, 1] * A[0, 2] * A[2, 0] - A[0, 1] * A[3, 2] * A[2, 0] - A[3, 1] * A[2, 2] * A[0, 0], # noqa: E501
- ], [
- A[3, 1] * A[1, 3] * A[0, 2] - A[0, 2] * A[1, 1] * A[3, 3] - A[3, 1] * A[0, 3] * A[1, 2] + A[3, 2] * A[1, 1] * A[0, 3] + A[0, 1] * A[3, 3] * A[1, 2] - A[0, 1] * A[1, 3] * A[3, 2], # noqa: E501
- A[1, 3] * A[3, 2] * A[0, 0] - A[1, 0] * A[3, 2] * A[0, 3] - A[1, 3] * A[0, 2] * A[3, 0] + A[0, 3] * A[3, 0] * A[1, 2] + A[1, 0] * A[0, 2] * A[3, 3] - A[3, 3] * A[0, 0] * A[1, 2], # noqa: E501
- -A[1, 0] * A[0, 1] * A[3, 3] + A[0, 1] * A[1, 3] * A[3, 0] - A[3, 1] * A[1, 3] * A[0, 0] - A[1, 1] * A[0, 3] * A[3, 0] + A[1, 0] * A[3, 1] * A[0, 3] + A[1, 1] * A[3, 3] * A[0, 0], # noqa: E501
- A[0, 2] * A[1, 1] * A[3, 0] - A[3, 2] * A[1, 1] * A[0, 0] - A[0, 1] * A[3, 0] * A[1, 2] - A[1, 0] * A[3, 1] * A[0, 2] + A[3, 1] * A[0, 0] * A[1, 2] + A[1, 0] * A[0, 1] * A[3, 2], # noqa: E501
- ], [
- A[0, 3] * A[2, 1] * A[1, 2] + A[0, 2] * A[2, 3] * A[1, 1] + A[0, 1] * A[2, 2] * A[1, 3] - A[2, 2] * A[1, 1] * A[0, 3] - A[1, 3] * A[0, 2] * A[2, 1] - A[0, 1] * A[2, 3] * A[1, 2], # noqa: E501
- A[1, 0] * A[2, 2] * A[0, 3] + A[1, 3] * A[0, 2] * A[2, 0] - A[1, 0] * A[0, 2] * A[2, 3] - A[2, 0] * A[0, 3] * A[1, 2] - A[2, 2] * A[1, 3] * A[0, 0] + A[2, 3] * A[0, 0] * A[1, 2], # noqa: E501
- -A[0, 1] * A[1, 3] * A[2, 0] + A[2, 0] * A[1, 1] * A[0, 3] + A[1, 3] * A[0, 0] * A[2, 1] - A[1, 0] * A[0, 3] * A[2, 1] + A[1, 0] * A[0, 1] * A[2, 3] - A[2, 3] * A[1, 1] * A[0, 0], # noqa: E501
- A[1, 0] * A[0, 2] * A[2, 1] - A[0, 2] * A[2, 0] * A[1, 1] + A[0, 1] * A[2, 0] * A[1, 2] + A[2, 2] * A[1, 1] * A[0, 0] - A[1, 0] * A[0, 1] * A[2, 2] - A[0, 0] * A[2, 1] * A[1, 2], # noqa: E501
- ]])
+ return as_matrix(
+ [
+ [
+ -A[3, 1] * A[2, 2] * A[1, 3]
+ - A[3, 2] * A[2, 3] * A[1, 1]
+ + A[1, 3] * A[3, 2] * A[2, 1]
+ + A[3, 1] * A[2, 3] * A[1, 2]
+ + A[2, 2] * A[1, 1] * A[3, 3]
+ - A[3, 3] * A[2, 1] * A[1, 2],
+ -A[1, 0] * A[2, 2] * A[3, 3]
+ + A[2, 0] * A[3, 3] * A[1, 2]
+ + A[2, 2] * A[1, 3] * A[3, 0]
+ - A[2, 3] * A[3, 0] * A[1, 2]
+ + A[1, 0] * A[3, 2] * A[2, 3]
+ - A[1, 3] * A[3, 2] * A[2, 0],
+ A[1, 0] * A[3, 3] * A[2, 1]
+ + A[2, 3] * A[1, 1] * A[3, 0]
+ - A[2, 0] * A[1, 1] * A[3, 3]
+ - A[1, 3] * A[3, 0] * A[2, 1]
+ - A[1, 0] * A[3, 1] * A[2, 3]
+ + A[3, 1] * A[1, 3] * A[2, 0],
+ A[3, 0] * A[2, 1] * A[1, 2]
+ + A[1, 0] * A[3, 1] * A[2, 2]
+ + A[3, 2] * A[2, 0] * A[1, 1]
+ - A[2, 2] * A[1, 1] * A[3, 0]
+ - A[3, 1] * A[2, 0] * A[1, 2]
+ - A[1, 0] * A[3, 2] * A[2, 1],
+ ],
+ [
+ A[3, 1] * A[2, 2] * A[0, 3]
+ + A[0, 2] * A[3, 3] * A[2, 1]
+ + A[0, 1] * A[3, 2] * A[2, 3]
+ - A[3, 1] * A[0, 2] * A[2, 3]
+ - A[0, 1] * A[2, 2] * A[3, 3]
+ - A[3, 2] * A[0, 3] * A[2, 1],
+ -A[2, 2] * A[0, 3] * A[3, 0]
+ - A[0, 2] * A[2, 0] * A[3, 3]
+ - A[3, 2] * A[2, 3] * A[0, 0]
+ + A[2, 2] * A[3, 3] * A[0, 0]
+ + A[0, 2] * A[2, 3] * A[3, 0]
+ + A[3, 2] * A[2, 0] * A[0, 3],
+ A[3, 1] * A[2, 3] * A[0, 0]
+ - A[0, 1] * A[2, 3] * A[3, 0]
+ - A[3, 1] * A[2, 0] * A[0, 3]
+ - A[3, 3] * A[0, 0] * A[2, 1]
+ + A[0, 3] * A[3, 0] * A[2, 1]
+ + A[0, 1] * A[2, 0] * A[3, 3],
+ A[3, 2] * A[0, 0] * A[2, 1]
+ - A[0, 2] * A[3, 0] * A[2, 1]
+ + A[0, 1] * A[2, 2] * A[3, 0]
+ + A[3, 1] * A[0, 2] * A[2, 0]
+ - A[0, 1] * A[3, 2] * A[2, 0]
+ - A[3, 1] * A[2, 2] * A[0, 0],
+ ],
+ [
+ A[3, 1] * A[1, 3] * A[0, 2]
+ - A[0, 2] * A[1, 1] * A[3, 3]
+ - A[3, 1] * A[0, 3] * A[1, 2]
+ + A[3, 2] * A[1, 1] * A[0, 3]
+ + A[0, 1] * A[3, 3] * A[1, 2]
+ - A[0, 1] * A[1, 3] * A[3, 2],
+ A[1, 3] * A[3, 2] * A[0, 0]
+ - A[1, 0] * A[3, 2] * A[0, 3]
+ - A[1, 3] * A[0, 2] * A[3, 0]
+ + A[0, 3] * A[3, 0] * A[1, 2]
+ + A[1, 0] * A[0, 2] * A[3, 3]
+ - A[3, 3] * A[0, 0] * A[1, 2],
+ -A[1, 0] * A[0, 1] * A[3, 3]
+ + A[0, 1] * A[1, 3] * A[3, 0]
+ - A[3, 1] * A[1, 3] * A[0, 0]
+ - A[1, 1] * A[0, 3] * A[3, 0]
+ + A[1, 0] * A[3, 1] * A[0, 3]
+ + A[1, 1] * A[3, 3] * A[0, 0],
+ A[0, 2] * A[1, 1] * A[3, 0]
+ - A[3, 2] * A[1, 1] * A[0, 0]
+ - A[0, 1] * A[3, 0] * A[1, 2]
+ - A[1, 0] * A[3, 1] * A[0, 2]
+ + A[3, 1] * A[0, 0] * A[1, 2]
+ + A[1, 0] * A[0, 1] * A[3, 2],
+ ],
+ [
+ A[0, 3] * A[2, 1] * A[1, 2]
+ + A[0, 2] * A[2, 3] * A[1, 1]
+ + A[0, 1] * A[2, 2] * A[1, 3]
+ - A[2, 2] * A[1, 1] * A[0, 3]
+ - A[1, 3] * A[0, 2] * A[2, 1]
+ - A[0, 1] * A[2, 3] * A[1, 2],
+ A[1, 0] * A[2, 2] * A[0, 3]
+ + A[1, 3] * A[0, 2] * A[2, 0]
+ - A[1, 0] * A[0, 2] * A[2, 3]
+ - A[2, 0] * A[0, 3] * A[1, 2]
+ - A[2, 2] * A[1, 3] * A[0, 0]
+ + A[2, 3] * A[0, 0] * A[1, 2],
+ -A[0, 1] * A[1, 3] * A[2, 0]
+ + A[2, 0] * A[1, 1] * A[0, 3]
+ + A[1, 3] * A[0, 0] * A[2, 1]
+ - A[1, 0] * A[0, 3] * A[2, 1]
+ + A[1, 0] * A[0, 1] * A[2, 3]
+ - A[2, 3] * A[1, 1] * A[0, 0],
+ A[1, 0] * A[0, 2] * A[2, 1]
+ - A[0, 2] * A[2, 0] * A[1, 1]
+ + A[0, 1] * A[2, 0] * A[1, 2]
+ + A[2, 2] * A[1, 1] * A[0, 0]
+ - A[1, 0] * A[0, 1] * A[2, 2]
+ - A[0, 0] * A[2, 1] * A[1, 2],
+ ],
+ ]
+ )
def deviatoric_expr(A):
@@ -302,12 +478,20 @@ def deviatoric_expr(A):
def deviatoric_expr_2x2(A):
"""Deviatoric of a 2 by 2 matrix."""
- return as_matrix([[-1. / 2 * A[1, 1] + 1. / 2 * A[0, 0], A[0, 1]],
- [A[1, 0], 1. / 2 * A[1, 1] - 1. / 2 * A[0, 0]]])
+ return as_matrix(
+ [
+ [-1.0 / 2 * A[1, 1] + 1.0 / 2 * A[0, 0], A[0, 1]],
+ [A[1, 0], 1.0 / 2 * A[1, 1] - 1.0 / 2 * A[0, 0]],
+ ]
+ )
def deviatoric_expr_3x3(A):
"""Deviatoric of a 3 by 3 matrix."""
- return as_matrix([[-1. / 3 * A[1, 1] - 1. / 3 * A[2, 2] + 2. / 3 * A[0, 0], A[0, 1], A[0, 2]],
- [A[1, 0], 2. / 3 * A[1, 1] - 1. / 3 * A[2, 2] - 1. / 3 * A[0, 0], A[1, 2]],
- [A[2, 0], A[2, 1], -1. / 3 * A[1, 1] + 2. / 3 * A[2, 2] - 1. / 3 * A[0, 0]]])
+ return as_matrix(
+ [
+ [-1.0 / 3 * A[1, 1] - 1.0 / 3 * A[2, 2] + 2.0 / 3 * A[0, 0], A[0, 1], A[0, 2]],
+ [A[1, 0], 2.0 / 3 * A[1, 1] - 1.0 / 3 * A[2, 2] - 1.0 / 3 * A[0, 0], A[1, 2]],
+ [A[2, 0], A[2, 1], -1.0 / 3 * A[1, 1] + 2.0 / 3 * A[2, 2] - 1.0 / 3 * A[0, 0]],
+ ]
+ )
diff --git a/ufl/conditional.py b/ufl/conditional.py
index 5d0fb910d..117808c2b 100644
--- a/ufl/conditional.py
+++ b/ufl/conditional.py
@@ -43,7 +43,7 @@ def __bool__(self):
class BinaryCondition(Condition):
"""Binary condition."""
- __slots__ = ('_name',)
+ __slots__ = ("_name",)
def __init__(self, name, left, right):
"""Initialise."""
@@ -54,13 +54,13 @@ def __init__(self, name, left, right):
self._name = name
- if name in ('!=', '=='):
+ if name in ("!=", "=="):
# Since equals and not-equals are used for comparing
# representations, we have to allow any shape here. The
# scalar properties must be checked when used in
# conditional instead!
pass
- elif name in ('&&', '||'):
+ elif name in ("&&", "||"):
# Binary operators acting on boolean expressions allow
# only conditions
for arg in (left, right):
@@ -76,8 +76,11 @@ def __init__(self, name, left, right):
def __str__(self):
"""Format as a string."""
- return "%s %s %s" % (parstr(self.ufl_operands[0], self),
- self._name, parstr(self.ufl_operands[1], self))
+ return "%s %s %s" % (
+ parstr(self.ufl_operands[0], self),
+ self._name,
+ parstr(self.ufl_operands[1], self),
+ )
# Not associating with __eq__, the concept of equality with == is
@@ -254,8 +257,7 @@ def __str__(self):
return "!(%s)" % (str(self.ufl_operands[0]),)
-@ufl_type(num_ops=3, inherit_shape_from_operand=1,
- inherit_indices_from_operand=1)
+@ufl_type(num_ops=3, inherit_shape_from_operand=1, inherit_indices_from_operand=1)
class Conditional(Operator):
"""Conditional expression.
@@ -279,10 +281,14 @@ def __init__(self, condition, true_value, false_value):
if tfi != ffi:
raise ValueError("Free index mismatch between conditional branches.")
if isinstance(condition, (EQ, NE)):
- if not all((condition.ufl_operands[0].ufl_shape == (),
- condition.ufl_operands[0].ufl_free_indices == (),
- condition.ufl_operands[1].ufl_shape == (),
- condition.ufl_operands[1].ufl_free_indices == ())):
+ if not all(
+ (
+ condition.ufl_operands[0].ufl_shape == (),
+ condition.ufl_operands[0].ufl_free_indices == (),
+ condition.ufl_operands[1].ufl_shape == (),
+ condition.ufl_operands[1].ufl_free_indices == (),
+ )
+ ):
raise ValueError("Non-scalar == or != is not allowed.")
Operator.__init__(self, (condition, true_value, false_value))
@@ -303,6 +309,7 @@ def __str__(self):
# --- Specific functions higher level than a conditional ---
+
@ufl_type(is_scalar=True, num_ops=1)
class MinValue(Operator):
"""Take the minimum of two values."""
@@ -323,7 +330,7 @@ def evaluate(self, x, mapping, component, index_values):
try:
res = min(a, b)
except ValueError:
- warnings.warn('Value error in evaluation of min() of %s and %s.' % self.ufl_operands)
+ warnings.warn("Value error in evaluation of min() of %s and %s." % self.ufl_operands)
raise
return res
@@ -352,7 +359,7 @@ def evaluate(self, x, mapping, component, index_values):
try:
res = max(a, b)
except ValueError:
- warnings.warn('Value error in evaluation of max() of %s and %s.' % self.ufl_operands)
+ warnings.warn("Value error in evaluation of max() of %s and %s." % self.ufl_operands)
raise
return res
diff --git a/ufl/constant.py b/ufl/constant.py
index 74eb81932..e9d658fcc 100644
--- a/ufl/constant.py
+++ b/ufl/constant.py
@@ -1,4 +1,4 @@
-"""This module defines classes representing non-literal values which are constant with respect to a domain."""
+"""Support fpr non-literal values which are constant with respect to a domain."""
# Copyright (C) 2019 Michal Habera
#
@@ -29,7 +29,8 @@ def __init__(self, domain, shape=(), count=None):
# Repr string is build in such way, that reconstruction
# with eval() is possible
self._repr = "Constant({}, {}, {})".format(
- repr(self._ufl_domain), repr(self._ufl_shape), repr(self._count))
+ repr(self._ufl_domain), repr(self._ufl_shape), repr(self._count)
+ )
@property
def ufl_shape(self):
@@ -42,7 +43,7 @@ def ufl_domain(self):
def ufl_domains(self):
"""Get the UFL domains."""
- return (self.ufl_domain(), )
+ return (self.ufl_domain(),)
def is_cellwise_constant(self):
"""Return True if the function is cellwise constant."""
@@ -62,23 +63,30 @@ def __eq__(self, other):
return False
if self is other:
return True
- return (self._count == other._count and self._ufl_domain == other._ufl_domain and # noqa: W504
- self._ufl_shape == self._ufl_shape)
+ return (
+ self._count == other._count
+ and self._ufl_domain == other._ufl_domain
+ and self._ufl_shape == self._ufl_shape
+ )
def _ufl_signature_data_(self, renumbering):
"""Signature data for constant depends on renumbering."""
return "Constant({}, {}, {})".format(
- self._ufl_domain._ufl_signature_data_(renumbering), repr(self._ufl_shape),
- repr(renumbering[self]))
+ self._ufl_domain._ufl_signature_data_(renumbering),
+ repr(self._ufl_shape),
+ repr(renumbering[self]),
+ )
def VectorConstant(domain, count=None):
"""Vector constant."""
domain = as_domain(domain)
- return Constant(domain, shape=(domain.geometric_dimension(), ), count=count)
+ return Constant(domain, shape=(domain.geometric_dimension(),), count=count)
def TensorConstant(domain, count=None):
"""Tensor constant."""
domain = as_domain(domain)
- return Constant(domain, shape=(domain.geometric_dimension(), domain.geometric_dimension()), count=count)
+ return Constant(
+ domain, shape=(domain.geometric_dimension(), domain.geometric_dimension()), count=count
+ )
diff --git a/ufl/constantvalue.py b/ufl/constantvalue.py
index 82d9ed84e..b918a670b 100644
--- a/ufl/constantvalue.py
+++ b/ufl/constantvalue.py
@@ -9,9 +9,11 @@
# Modified by Anders Logg, 2011.
# Modified by Massimiliano Leoni, 2016.
+import numbers
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
@@ -33,6 +35,7 @@ def format_float(x):
# --- Base classes for constant types ---
+
@ufl_type(is_abstract=True)
class ConstantValue(Terminal):
"""Constant value."""
@@ -99,16 +102,23 @@ def _init(self, shape=(), free_indices=(), index_dimensions=None):
self.ufl_free_indices = ()
self.ufl_index_dimensions = ()
elif all(isinstance(i, Index) for i in free_indices): # Handle old input format
- if not isinstance(index_dimensions, dict) and all(isinstance(i, Index) for i in index_dimensions.keys()):
+ if not isinstance(index_dimensions, dict) and all(
+ isinstance(i, Index) for i in index_dimensions.keys()
+ ):
raise ValueError(f"Expecting tuple of index dimensions, not {index_dimensions}")
self.ufl_free_indices = tuple(sorted(i.count() for i in free_indices))
self.ufl_index_dimensions = tuple(
- d for i, d in sorted(index_dimensions.items(), key=lambda x: x[0].count()))
+ d for i, d in sorted(index_dimensions.items(), key=lambda x: x[0].count())
+ )
else: # Handle new input format
if not all(isinstance(i, int) for i in free_indices):
raise ValueError(f"Expecting tuple of integer free index ids, not {free_indices}")
- if not isinstance(index_dimensions, tuple) and all(isinstance(i, int) for i in index_dimensions):
- raise ValueError(f"Expecting tuple of integer index dimensions, not {index_dimensions}")
+ if not isinstance(index_dimensions, tuple) and all(
+ isinstance(i, int) for i in index_dimensions
+ ):
+ raise ValueError(
+ f"Expecting tuple of integer index dimensions, not {index_dimensions}"
+ )
# Assuming sorted now to avoid this cost, enable for debugging:
# if sorted(free_indices) != list(free_indices):
@@ -136,7 +146,8 @@ def __repr__(self):
r = "Zero(%s, %s, %s)" % (
repr(self.ufl_shape),
repr(self.ufl_free_indices),
- repr(self.ufl_index_dimensions))
+ repr(self.ufl_index_dimensions),
+ )
return r
def __eq__(self, other):
@@ -144,9 +155,11 @@ def __eq__(self, other):
if isinstance(other, Zero):
if self is other:
return True
- return (self.ufl_shape == other.ufl_shape and # noqa: W504
- self.ufl_free_indices == other.ufl_free_indices and # noqa: W504
- self.ufl_index_dimensions == other.ufl_index_dimensions)
+ return (
+ self.ufl_shape == other.ufl_shape
+ and self.ufl_free_indices == other.ufl_free_indices
+ and self.ufl_index_dimensions == other.ufl_index_dimensions
+ )
elif isinstance(other, (int, float)):
return other == 0
else:
@@ -189,6 +202,7 @@ def zero(*shape):
# --- Scalar value types ---
+
@ufl_type(is_abstract=True, is_scalar=True)
class ScalarValue(ConstantValue):
"""A constant scalar value."""
@@ -345,6 +359,7 @@ def __repr__(self):
@ufl_type(wraps_type=int, is_literal=True)
class IntValue(RealValue):
"""Representation of a constant scalar integer value."""
+
__slots__ = ()
_cache = {}
@@ -387,9 +402,11 @@ def __repr__(self):
# --- Identity matrix ---
+
@ufl_type()
class Identity(ConstantValue):
"""Representation of an identity matrix."""
+
__slots__ = ("_dim", "ufl_shape")
def __init__(self, dim):
@@ -427,6 +444,7 @@ def __eq__(self, other):
# --- Permutation symbol ---
+
@ufl_type()
class PermutationSymbol(ConstantValue):
"""Representation of a permutation symbol.
@@ -489,12 +507,13 @@ def as_ufl(expression):
"""Converts expression to an Expr if possible."""
if isinstance(expression, (Expr, ufl.BaseForm)):
return expression
- elif isinstance(expression, complex):
- return ComplexValue(expression)
- elif isinstance(expression, float):
- return FloatValue(expression)
- elif isinstance(expression, int):
+ elif isinstance(expression, numbers.Integral):
return IntValue(expression)
+ elif isinstance(expression, numbers.Real):
+ return FloatValue(expression)
+ elif isinstance(expression, numbers.Complex):
+ return ComplexValue(expression)
else:
raise ValueError(
- f"Invalid type conversion: {expression} can not be converted to any UFL type.")
+ f"Invalid type conversion: {expression} can not be converted to any UFL type."
+ )
diff --git a/ufl/core/base_form_operator.py b/ufl/core/base_form_operator.py
index a136244cb..aae943f75 100644
--- a/ufl/core/base_form_operator.py
+++ b/ufl/core/base_form_operator.py
@@ -1,7 +1,8 @@
"""Base form operator.
-This module defines the BaseFormOperator class, which is the base class for objects that can be seen as forms
-and as operators such as ExternalOperator or Interpolate.
+This module defines the BaseFormOperator class, which is the base class
+for objects that can be seen as forms and as operators such as
+ExternalOperator or Interpolate.
"""
# Copyright (C) 2019 Nacime Bouziani
@@ -36,9 +37,11 @@ def __init__(self, *operands, function_space, derivatives=None, argument_slots=(
Args:
operands: operands on which acts the operator.
- function_space: the FunctionSpace or MixedFunctionSpace on which to build this Function.
- derivatives: tuple specifiying the derivative multiindex.
- argument_slots: tuple composed containing expressions with ufl.Argument or ufl.Coefficient objects.
+ function_space: the FunctionSpace or MixedFunctionSpace on
+ which to build this Function.
+ derivatives: tuple specifying the derivative multiindex.
+ argument_slots: tuple composed containing expressions with
+ ufl.Argument or ufl.Coefficient objects.
"""
BaseForm.__init__(self)
ufl_operands = tuple(map(as_ufl, operands))
@@ -73,12 +76,14 @@ def __init__(self, *operands, function_space, derivatives=None, argument_slots=(
def argument_slots(self, outer_form=False):
"""Returns a tuple of expressions containing argument and coefficient based expressions.
- We get an argument uhat when we take the Gateaux derivative in the direction uhat:
- d/du N(u; v*) = dNdu(u; uhat, v*) where uhat is a ufl.Argument and v* a ufl.Coargument
- Applying the action replace the last argument by coefficient:
- action(dNdu(u; uhat, v*), w) = dNdu(u; w, v*) where du is a ufl.Coefficient.
+ We get an argument uhat when we take the Gateaux derivative in
+ the direction uhat: d/du N(u; v*) = dNdu(u; uhat, v*) where uhat
+ is a ufl.Argument and v* a ufl.Coargument Applying the action
+ replace the last argument by coefficient: action(dNdu(u; uhat,
+ v*), w) = dNdu(u; w, v*) where du is a ufl.Coefficient.
"""
from ufl.algorithms.analysis import extract_arguments
+
if not outer_form:
return self._argument_slots
# Takes into account argument contraction when a base form operator is in an outer form:
@@ -96,14 +101,19 @@ def coefficients(self):
def _analyze_form_arguments(self):
"""Analyze which Argument and Coefficient objects can be found in the base form."""
from ufl.algorithms.analysis import extract_arguments, extract_coefficients, extract_type
+
dual_arg, *arguments = self.argument_slots()
- # When coarguments are treated as BaseForms, they have two arguments (one primal and one dual)
- # as they map from V* to V* => V* x V -> R. However, when they are treated as mere "arguments",
- # the primal space argument is discarded and we only have the dual space argument (Coargument).
- # This is the exact same situation than BaseFormOperator's arguments which are different depending on
- # whether the BaseFormOperator is used in an outer form or not.
- arguments = (tuple(extract_type(dual_arg, Coargument))
- + tuple(a for arg in arguments for a in extract_arguments(arg)))
+ # When coarguments are treated as BaseForms, they have two
+ # arguments (one primal and one dual) as they map from V* to V*
+ # => V* x V -> R. However, when they are treated as mere
+ # "arguments", the primal space argument is discarded and we
+ # only have the dual space argument (Coargument). This is the
+ # exact same situation than BaseFormOperator's arguments which
+ # are different depending on whether the BaseFormOperator is
+ # used in an outer form or not.
+ arguments = tuple(extract_type(dual_arg, Coargument)) + tuple(
+ a for arg in arguments for a in extract_arguments(arg)
+ )
coefficients = tuple(c for op in self.ufl_operands for c in extract_coefficients(op))
# Define canonical numbering of arguments and coefficients
# 1) Need concept of order since we may have arguments with the same number
@@ -123,20 +133,32 @@ def count(self):
@property
def ufl_shape(self):
"""Return the UFL shape of the coefficient.produced by the operator."""
- return self.arguments()[0]._ufl_shape
+ arg, *_ = self.argument_slots()
+ if isinstance(arg, BaseForm):
+ arg, *_ = arg.arguments()
+ return arg._ufl_shape
def ufl_function_space(self):
"""Return the function space associated to the operator.
I.e. return the dual of the base form operator's Coargument.
"""
- return self.arguments()[0]._ufl_function_space.dual()
-
- def _ufl_expr_reconstruct_(self, *operands, function_space=None, derivatives=None, argument_slots=None):
+ arg, *_ = self.argument_slots()
+ if isinstance(arg, BaseForm):
+ arg, *_ = arg.arguments()
+ return arg._ufl_function_space
+ return arg._ufl_function_space.dual()
+
+ def _ufl_expr_reconstruct_(
+ self, *operands, function_space=None, derivatives=None, argument_slots=None
+ ):
"""Return a new object of the same type with new operands."""
- return type(self)(*operands, function_space=function_space or self.ufl_function_space(),
- derivatives=derivatives or self.derivatives,
- argument_slots=argument_slots or self.argument_slots())
+ return type(self)(
+ *operands,
+ function_space=function_space or self.ufl_function_space(),
+ derivatives=derivatives or self.derivatives,
+ argument_slots=argument_slots or self.argument_slots(),
+ )
def __repr__(self):
"""Default repr string construction for base form operators."""
@@ -149,11 +171,13 @@ def __repr__(self):
def __hash__(self):
"""Hash code for use in dicts."""
- hashdata = (type(self),
- tuple(hash(op) for op in self.ufl_operands),
- tuple(hash(arg) for arg in self._argument_slots),
- self.derivatives,
- hash(self.ufl_function_space()))
+ hashdata = (
+ type(self),
+ tuple(hash(op) for op in self.ufl_operands),
+ tuple(hash(arg) for arg in self._argument_slots),
+ self.derivatives,
+ hash(self.ufl_function_space()),
+ )
return hash(hashdata)
def __eq__(self, other):
diff --git a/ufl/core/expr.py b/ufl/core/expr.py
index e6b2ce3d8..cef955f0e 100644
--- a/ufl/core/expr.py
+++ b/ufl/core/expr.py
@@ -169,13 +169,10 @@ def __init__(self):
_ufl_required_properties_ = (
# A tuple of operands, all of them Expr instances.
"ufl_operands",
-
# A tuple of ints, the value shape of the expression.
"ufl_shape",
-
# A tuple of free index counts.
"ufl_free_indices",
-
# A tuple providing the int dimension for each free index.
"ufl_index_dimensions",
)
@@ -186,24 +183,19 @@ def __init__(self):
_ufl_required_methods_ = (
# To compute the hash on demand, this method is called.
"_ufl_compute_hash_",
-
# The data returned from this method is used to compute the
# signature of a form
"_ufl_signature_data_",
-
# The == operator must be implemented to compare for identical
# representation, used by set() and dict(). The __hash__
# operator is added by ufl_type.
"__eq__",
-
# To reconstruct an object of the same type with operands or
# properties changed.
"_ufl_expr_reconstruct_", # Implemented in Operator and Terminal so this should never fail
-
"ufl_domains",
# "ufl_cell",
# "ufl_domain",
-
# "__str__",
# "__repr__",
)
@@ -259,16 +251,22 @@ def _ufl_expr_reconstruct_(self, *operands):
def ufl_domains(self):
"""Return all domains this expression is defined on."""
- warnings.warn("Expr.ufl_domains() is deprecated, please "
- "use extract_domains(expr) instead.", DeprecationWarning)
+ warnings.warn(
+ "Expr.ufl_domains() is deprecated, please use extract_domains(expr) instead.",
+ DeprecationWarning,
+ )
from ufl.domain import extract_domains
+
return extract_domains(self)
def ufl_domain(self):
"""Return the single unique domain this expression is defined on, or throw an error."""
- warnings.warn("Expr.ufl_domain() is deprecated, please "
- "use extract_unique_domain(expr) instead.", DeprecationWarning)
+ warnings.warn(
+ "Expr.ufl_domain() is deprecated, please use extract_unique_domain(expr) instead.",
+ DeprecationWarning,
+ )
from ufl.domain import extract_unique_domain
+
return extract_unique_domain(self)
# --- Functions for float evaluation ---
diff --git a/ufl/core/external_operator.py b/ufl/core/external_operator.py
index ecd736391..3a9304854 100644
--- a/ufl/core/external_operator.py
+++ b/ufl/core/external_operator.py
@@ -1,8 +1,9 @@
"""External operator.
-This module defines the ``ExternalOperator`` class, which symbolically represents operators that are not
-straightforwardly expressible in UFL. Subclasses of ``ExternalOperator`` must define
-how this operator should be evaluated as well as its derivatives from a given set of operands.
+This module defines the ``ExternalOperator`` class, which symbolically
+represents operators that are not straightforwardly expressible in UFL.
+Subclasses of ``ExternalOperator`` must define how this operator should
+be evaluated as well as its derivatives from a given set of operands.
"""
# Copyright (C) 2019 Nacime Bouziani
#
@@ -29,26 +30,34 @@ def __init__(self, *operands, function_space, derivatives=None, argument_slots=(
Args:
operands: operands on which acts the ExternalOperator.
- function_space: the FunctionSpace, or MixedFunctionSpace on which to build this Function.
- derivatives: tuple specifiying the derivative multiindex.
- argument_slots: tuple composed containing expressions with ufl.Argument or ufl.Coefficient objects.
+ function_space: the FunctionSpace, or MixedFunctionSpace on
+ which to build this Function.
+ derivatives: tuple specifying the derivative multiindex.
+ argument_slots: tuple composed containing expressions with
+ ufl.Argument or ufl.Coefficient objects.
+ argument_slots: TODO
"""
# -- Derivatives -- #
if derivatives is not None:
if not isinstance(derivatives, tuple):
- raise TypeError(f"Expecting a tuple for derivatives and not {derivatives}")
+ raise TypeError(f"Expecting a tuple for derivatives and not {derivatives}.")
if not len(derivatives) == len(operands):
- raise ValueError(f"Expecting a size of {len(operands)} for {derivatives}")
+ raise ValueError(f"Expecting a size of {len(operands)} for {derivatives}.")
if not all(isinstance(d, int) for d in derivatives) or any(d < 0 for d in derivatives):
raise ValueError(
- f"Expecting a derivative multi-index with nonnegative indices and not {str(derivatives)}")
+ "Expecting a derivative multi-index with nonnegative indices, "
+ f"not {derivatives}."
+ )
else:
derivatives = (0,) * len(operands)
- BaseFormOperator.__init__(self, *operands,
- function_space=function_space,
- derivatives=derivatives,
- argument_slots=argument_slots)
+ BaseFormOperator.__init__(
+ self,
+ *operands,
+ function_space=function_space,
+ derivatives=derivatives,
+ argument_slots=argument_slots,
+ )
def ufl_element(self):
"""Shortcut to get the finite element of the function space of the external operator."""
@@ -57,28 +66,38 @@ def ufl_element(self):
def grad(self):
"""Returns the symbolic grad of the external operator."""
- # By default, differential rules produce `grad(assembled_o)` `where assembled_o`
- # is the `Coefficient` resulting from assembling the external operator since
- # the external operator may not be smooth enough for chain rule to hold.
- # Symbolic gradient (`grad(ExternalOperator)`) depends on the operator considered
- # and its implementation may be needed in some cases (e.g. convolution operator).
- raise NotImplementedError('Symbolic gradient not defined for the external operator considered!')
+ # By default, differential rules produce `grad(assembled_o)`
+ # `where assembled_o` is the `Coefficient` resulting from
+ # assembling the external operator since the external operator
+ # may not be smooth enough for chain rule to hold. Symbolic
+ # gradient (`grad(ExternalOperator)`) depends on the operator
+ # considered and its implementation may be needed in some cases
+ # (e.g. convolution operator).
+ raise NotImplementedError(
+ "Symbolic gradient not defined for the external operator considered."
+ )
def assemble(self, *args, **kwargs):
"""Assemble the external operator."""
- raise NotImplementedError(f"Symbolic evaluation of {self._ufl_class_.__name__} not available.")
+ raise NotImplementedError(
+ f"Symbolic evaluation of {self._ufl_class_.__name__} not available."
+ )
- def _ufl_expr_reconstruct_(self, *operands, function_space=None, derivatives=None,
- argument_slots=None, add_kwargs={}):
+ def _ufl_expr_reconstruct_(
+ self, *operands, function_space=None, derivatives=None, argument_slots=None, add_kwargs={}
+ ):
"""Return a new object of the same type with new operands."""
- return type(self)(*operands, function_space=function_space or self.ufl_function_space(),
- derivatives=derivatives or self.derivatives,
- argument_slots=argument_slots or self.argument_slots(),
- **add_kwargs)
+ return type(self)(
+ *operands,
+ function_space=function_space or self.ufl_function_space(),
+ derivatives=derivatives or self.derivatives,
+ argument_slots=argument_slots or self.argument_slots(),
+ **add_kwargs,
+ )
def __str__(self):
"""Default str string for ExternalOperator operators."""
- d = '\N{PARTIAL DIFFERENTIAL}'
+ d = "\N{PARTIAL DIFFERENTIAL}"
derivatives = self.derivatives
d_ops = "".join(d + "o" + str(i + 1) for i, di in enumerate(derivatives) for j in range(di))
e = "e("
@@ -92,8 +111,10 @@ def __eq__(self, other):
"""Check for equality."""
if self is other:
return True
- return (type(self) is type(other) and
- all(a == b for a, b in zip(self.ufl_operands, other.ufl_operands)) and
- all(a == b for a, b in zip(self._argument_slots, other._argument_slots)) and
- self.derivatives == other.derivatives and
- self.ufl_function_space() == other.ufl_function_space())
+ return (
+ type(self) is type(other)
+ and all(a == b for a, b in zip(self.ufl_operands, other.ufl_operands))
+ and all(a == b for a, b in zip(self._argument_slots, other._argument_slots))
+ and self.derivatives == other.derivatives
+ and self.ufl_function_space() == other.ufl_function_space()
+ )
diff --git a/ufl/core/interpolate.py b/ufl/core/interpolate.py
index 86198c680..3b5e3ba4d 100644
--- a/ufl/core/interpolate.py
+++ b/ufl/core/interpolate.py
@@ -8,13 +8,14 @@
#
# Modified by Nacime Bouziani, 2021-2022
+from ufl.action import Action
from ufl.argument import Argument, Coargument
from ufl.coefficient import Cofunction
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.form import BaseForm, Form
from ufl.functionspace import AbstractFunctionSpace
@@ -35,14 +36,16 @@ def __init__(self, expr, v):
defined on the dual of the FunctionSpace to interpolate into.
"""
# This check could be more rigorous.
- dual_args = (Coargument, Cofunction, Form)
+ dual_args = (Coargument, Cofunction, Form, Action, BaseFormOperator)
if isinstance(v, AbstractFunctionSpace):
if is_dual(v):
- raise ValueError('Expecting a primal function space.')
+ raise ValueError("Expecting a primal function space.")
v = Argument(v.dual(), 0)
elif not isinstance(v, dual_args):
- raise ValueError("Expecting the second argument to be FunctionSpace, FiniteElement or dual.")
+ raise ValueError(
+ "Expecting the second argument to be FunctionSpace, FiniteElement or dual."
+ )
expr = as_ufl(expr)
if isinstance(expr, dual_args):
@@ -51,12 +54,16 @@ def __init__(self, expr, v):
# Reversed order convention
argument_slots = (v, expr)
# Get the primal space (V** = V)
- vv = v if not isinstance(v, Form) else v.arguments()[0]
- function_space = vv.ufl_function_space().dual()
+ if isinstance(v, BaseForm):
+ arg, *_ = v.arguments()
+ function_space = arg.ufl_function_space()
+ else:
+ function_space = v.ufl_function_space().dual()
# Set the operand as `expr` for DAG traversal purpose.
operand = expr
- BaseFormOperator.__init__(self, operand, function_space=function_space,
- argument_slots=argument_slots)
+ BaseFormOperator.__init__(
+ self, operand, function_space=function_space, argument_slots=argument_slots
+ )
def _ufl_expr_reconstruct_(self, expr, v=None, **add_kwargs):
"""Return a new object of the same type with new operands."""
@@ -81,9 +88,11 @@ def __eq__(self, other):
"""Check for equality."""
if self is other:
return True
- return (type(self) is type(other) and
- all(a == b for a, b in zip(self._argument_slots, other._argument_slots)) and
- self.ufl_function_space() == other.ufl_function_space())
+ return (
+ type(self) is type(other)
+ and all(a == b for a, b in zip(self._argument_slots, other._argument_slots))
+ and self.ufl_function_space() == other.ufl_function_space()
+ )
# Helper function
diff --git a/ufl/core/multiindex.py b/ufl/core/multiindex.py
index 81409ce7d..2d91e40f3 100644
--- a/ufl/core/multiindex.py
+++ b/ufl/core/multiindex.py
@@ -8,7 +8,6 @@
#
# Modified by Massimiliano Leoni, 2016.
-
from ufl.core.terminal import Terminal
from ufl.core.ufl_type import ufl_type
from ufl.utils.counted import Counted
@@ -19,6 +18,7 @@
class IndexBase(object):
"""Base class for all indices."""
+
__slots__ = ()
def __init__(self):
@@ -27,6 +27,7 @@ def __init__(self):
class FixedIndex(IndexBase):
"""UFL value: An index with a specific value assigned."""
+
__slots__ = ("_value", "_hash")
_cache = {}
@@ -112,6 +113,7 @@ def __repr__(self):
@ufl_type()
class MultiIndex(Terminal):
"""Represents a sequence of indices, either fixed or free."""
+
__slots__ = ("_indices",)
_cache = {}
@@ -164,8 +166,7 @@ def _ufl_compute_hash_(self):
def __eq__(self, other):
"""Check equality."""
- return isinstance(other, MultiIndex) and \
- self._indices == other._indices
+ return isinstance(other, MultiIndex) and self._indices == other._indices
def evaluate(self, x, mapping, component, index_values):
"""Evaluate index."""
diff --git a/ufl/core/terminal.py b/ufl/core/terminal.py
index f258a1c04..47c6c16d9 100644
--- a/ufl/core/terminal.py
+++ b/ufl/core/terminal.py
@@ -54,10 +54,12 @@ def evaluate(self, x, mapping, component, index_values, derivatives=()):
except Exception:
pass
# If it has an ufl_evaluate function, call it
- if hasattr(self, 'ufl_evaluate'):
+ if hasattr(self, "ufl_evaluate"):
return self.ufl_evaluate(x, component, derivatives)
# Take component if any
- warnings.warn(f"Couldn't map '{self}' to a float, returning ufl object without evaluation.")
+ warnings.warn(
+ f"Couldn't map '{self}' to a float, returning ufl object without evaluation."
+ )
f = self
if component:
f = f[component]
@@ -93,9 +95,11 @@ def __eq__(self, other):
# --- Subgroups of terminals ---
+
@ufl_type(is_abstract=True)
class FormArgument(Terminal):
"""An abstract class for a form argument (a thing in a primal finite element space)."""
+
__slots__ = ()
def __init__(self):
diff --git a/ufl/core/ufl_id.py b/ufl/core/ufl_id.py
index bc1206761..34cbda859 100644
--- a/ufl/core/ufl_id.py
+++ b/ufl/core/ufl_id.py
@@ -51,6 +51,7 @@ def init_ufl_id(self, ufl_id):
ufl_id = cls._ufl_global_id
cls._ufl_global_id = max(ufl_id, cls._ufl_global_id) + 1
return ufl_id
+
return init_ufl_id
# Modify class:
diff --git a/ufl/core/ufl_type.py b/ufl/core/ufl_type.py
index e19a9c340..c409d05fb 100644
--- a/ufl/core/ufl_type.py
+++ b/ufl/core/ufl_type.py
@@ -59,7 +59,8 @@ def get_base_attr(cls, name):
def set_trait(cls, basename, value, inherit=False):
"""Assign a trait to class with namespacing ``_ufl_basename_`` applied.
- If trait value is ``None``, optionally inherit it from the closest base class that has it.
+ If trait value is ``None``, optionally inherit it from the closest
+ base class that has it.
"""
name = "_ufl_" + basename + "_"
if value is None and inherit:
@@ -88,14 +89,18 @@ def determine_num_ops(cls, num_ops, unop, binop, rbinop):
def check_is_terminal_consistency(cls):
"""Check for consistency in ``is_terminal`` trait among superclasses."""
if cls._ufl_is_terminal_ is None:
- msg = (f"Class {cls.__name__} has not specified the is_terminal trait."
- " Did you forget to inherit from Terminal or Operator?")
+ msg = (
+ f"Class {cls.__name__} has not specified the is_terminal trait."
+ " Did you forget to inherit from Terminal or Operator?"
+ )
raise TypeError(msg)
base_is_terminal = get_base_attr(cls, "_ufl_is_terminal_")
if base_is_terminal is not None and cls._ufl_is_terminal_ != base_is_terminal:
- msg = (f"Conflicting given and automatic 'is_terminal' trait for class {cls.__name__}."
- " Check if you meant to inherit from Terminal or Operator.")
+ msg = (
+ f"Conflicting given and automatic 'is_terminal' trait for class {cls.__name__}."
+ " Check if you meant to inherit from Terminal or Operator."
+ )
raise TypeError(msg)
@@ -105,8 +110,10 @@ def check_abstract_trait_consistency(cls):
if base is core.expr.Expr:
break
if not issubclass(base, core.expr.Expr) and base._ufl_is_abstract_:
- msg = ("Base class {0.__name__} of class {1.__name__} "
- "is not an abstract subclass of {2.__name__}.")
+ msg = (
+ "Base class {0.__name__} of class {1.__name__} "
+ "is not an abstract subclass of {2.__name__}."
+ )
raise TypeError(msg.format(base, cls, core.expr.Expr))
@@ -116,15 +123,16 @@ def check_has_slots(cls):
return
if "__slots__" not in cls.__dict__:
- msg = ("Class {0.__name__} is missing the __slots__ "
- "attribute and is not marked with _ufl_noslots_.")
+ msg = (
+ "Class {0.__name__} is missing the __slots__ "
+ "attribute and is not marked with _ufl_noslots_."
+ )
raise TypeError(msg.format(cls))
# Check base classes for __slots__ as well, skipping object which is the last one
for base in cls.mro()[1:-1]:
if "__slots__" not in base.__dict__:
- msg = ("Class {0.__name__} is has a base class "
- "{1.__name__} with __slots__ missing.")
+ msg = "Class {0.__name__} is has a base class {1.__name__} with __slots__ missing."
raise TypeError(msg.format(cls, base))
@@ -202,22 +210,30 @@ def attach_implementations_of_indexing_interface(
# operands. This simplifies refactoring because a lot of types do
# this.
if inherit_shape_from_operand is not None:
+
def _inherited_ufl_shape(self):
return self.ufl_operands[inherit_shape_from_operand].ufl_shape
+
cls.ufl_shape = property(_inherited_ufl_shape)
if inherit_indices_from_operand is not None:
+
def _inherited_ufl_free_indices(self):
return self.ufl_operands[inherit_indices_from_operand].ufl_free_indices
def _inherited_ufl_index_dimensions(self):
return self.ufl_operands[inherit_indices_from_operand].ufl_index_dimensions
+
cls.ufl_free_indices = property(_inherited_ufl_free_indices)
cls.ufl_index_dimensions = property(_inherited_ufl_index_dimensions)
def update_global_expr_attributes(cls):
- """Update global ``Expr`` attributes, mainly by adding *cls* to global collections of ufl types."""
+ """Update global attributres.
+
+ Update global ``Expr`` attributes, mainly by adding *cls* to global
+ collections of ufl types.
+ """
if cls._ufl_is_terminal_modifier_:
core.expr.Expr._ufl_terminal_modifiers_.append(cls)
@@ -249,20 +265,34 @@ def update_ufl_type_attributes(cls):
def ufl_type(
- is_abstract=False, is_terminal=None, is_scalar=False, is_index_free=False, is_shaping=False,
- is_literal=False, is_terminal_modifier=False, is_in_reference_frame=False, is_restriction=False,
- is_evaluation=False, is_differential=None, use_default_hash=True, num_ops=None,
- inherit_shape_from_operand=None, inherit_indices_from_operand=None, wraps_type=None, unop=None,
- binop=None, rbinop=None
+ is_abstract=False,
+ is_terminal=None,
+ is_scalar=False,
+ is_index_free=False,
+ is_shaping=False,
+ is_literal=False,
+ is_terminal_modifier=False,
+ is_in_reference_frame=False,
+ is_restriction=False,
+ is_evaluation=False,
+ is_differential=None,
+ use_default_hash=True,
+ num_ops=None,
+ inherit_shape_from_operand=None,
+ inherit_indices_from_operand=None,
+ wraps_type=None,
+ unop=None,
+ binop=None,
+ rbinop=None,
):
- """This decorator is to be applied to every subclass in the UFL ``Expr`` and ``BaseForm`` hierarchy.
+ """Decorator to apply to every subclass in the UFL ``Expr`` and ``BaseForm`` hierarchy.
- This decorator contains a number of checks that are
- intended to enforce uniform behaviour across UFL types.
+ This decorator contains a number of checks that are intended to
+ enforce uniform behaviour across UFL types.
- The rationale behind the checks and the meaning of the
- optional arguments should be sufficiently documented
- in the source code below.
+ The rationale behind the checks and the meaning of the optional
+ arguments should be sufficiently documented in the source code
+ below.
"""
def _ufl_type_decorator_(cls):
@@ -285,11 +315,9 @@ def _ufl_type_decorator_(cls):
set_trait(cls, "is_terminal", is_terminal, inherit=True)
set_trait(cls, "is_literal", is_literal, inherit=True)
- set_trait(cls, "is_terminal_modifier", is_terminal_modifier,
- inherit=True)
+ set_trait(cls, "is_terminal_modifier", is_terminal_modifier, inherit=True)
set_trait(cls, "is_shaping", is_shaping, inherit=True)
- set_trait(cls, "is_in_reference_frame", is_in_reference_frame,
- inherit=True)
+ set_trait(cls, "is_in_reference_frame", is_in_reference_frame, inherit=True)
set_trait(cls, "is_restriction", is_restriction, inherit=True)
set_trait(cls, "is_evaluation", is_evaluation, inherit=True)
set_trait(cls, "is_differential", is_differential, inherit=True)
@@ -305,7 +333,8 @@ def _ufl_type_decorator_(cls):
"""# These are currently handled in the as_ufl implementation in constantvalue.py
if wraps_type is not None:
if not isinstance(wraps_type, type):
- msg = "Expecting a type, not a {0.__name__} for the wraps_type argument in definition of {1.__name__}."
+ msg = "Expecting a type, not a {0.__name__} for the
+ wraps_type argument in definition of {1.__name__}."
raise TypeError(msg.format(type(wraps_type), cls))
def _ufl_from_type_(value):
@@ -352,9 +381,9 @@ def _ufl_expr_rbinop_(self, other):
# class! This approach significantly reduces the amount of
# small functions to implement across all the types but of
# course it's a bit more opaque.
- attach_implementations_of_indexing_interface(cls,
- inherit_shape_from_operand,
- inherit_indices_from_operand)
+ attach_implementations_of_indexing_interface(
+ cls, inherit_shape_from_operand, inherit_indices_from_operand
+ )
# Update Expr
update_global_expr_attributes(cls)
diff --git a/ufl/corealg/map_dag.py b/ufl/corealg/map_dag.py
index f299130c1..9b9196f17 100644
--- a/ufl/corealg/map_dag.py
+++ b/ufl/corealg/map_dag.py
@@ -12,10 +12,10 @@
from ufl.corealg.traversal import cutoff_unique_post_traversal, unique_post_traversal
-def map_expr_dag(function, expression, compress=True, vcache=None, rcache=None):
+def map_expr_dag(function, expression, compress=True, vcache=None, rcache=None):
"""Apply a function to each subexpression node in an expression DAG.
- If the same funtion is called multiple times in a transformation
+ If the same function is called multiple times in a transformation
(as for example in apply_derivatives), then to reuse caches across
the call, use the arguments vcache and rcache.
@@ -25,32 +25,33 @@ def map_expr_dag(function, expression, compress=True, vcache=None, rcache=None)
compress: If True (default), the output object from
the function is cached in a dict and reused such that the
resulting expression DAG does not contain duplicate objects
- vcache: Optional dict for caching results of intermediate transformations
+ vcache: Optional dict for caching results of intermediate
+ transformations
rcache: Optional dict for caching results for compression
Returns:
The result of the final function call
"""
- result, = map_expr_dags(function, [expression], compress=compress,
- vcache=vcache,
- rcache=rcache)
+ (result,) = map_expr_dags(
+ function, [expression], compress=compress, vcache=vcache, rcache=rcache
+ )
return result
def map_expr_dags(function, expressions, compress=True, vcache=None, rcache=None):
- """Apply a function to each subexpression node in an expression DAG.
+ """Apply a function to each sub-expression node in an expression DAG.
If *compress* is ``True`` (default) the output object from
the function is cached in a ``dict`` and reused such that the
resulting expression DAG does not contain duplicate objects.
- If the same funtion is called multiple times in a transformation
+ If the same function is called multiple times in a transformation
(as for example in apply_derivatives), then to reuse caches across
the call, use the arguments vcache and rcache.
Args:
function: The function
- expression: An expression
+ expressions: An expression
compress: If True (default), the output object from
the function is cached in a dict and reused such that the
resulting expression DAG does not contain duplicate objects
@@ -80,9 +81,11 @@ def map_expr_dags(function, expressions, compress=True, vcache=None, rcache=None
# Pick faster traversal algorithm if we have no cutoffs
if any(cutoff_types):
+
def traversal(expression):
return cutoff_unique_post_traversal(expression, cutoff_types, visited)
else:
+
def traversal(expression):
return unique_post_traversal(expression, visited)
diff --git a/ufl/corealg/multifunction.py b/ufl/corealg/multifunction.py
index 1c57c2928..fc6e45136 100644
--- a/ufl/corealg/multifunction.py
+++ b/ufl/corealg/multifunction.py
@@ -29,6 +29,7 @@ def _memoized_handler(self, o):
r = handler(self, o)
c[o] = r
return r
+
return _memoized_handler
@@ -76,8 +77,7 @@ def __init__(self):
if hasattr(self, handler_name):
handler_names[classobject._ufl_typecode_] = handler_name
break
- is_cutoff_type = [get_num_args(getattr(self, name)) == 2
- for name in handler_names]
+ is_cutoff_type = [get_num_args(getattr(self, name)) == 2 for name in handler_names]
cache_data = (handler_names, is_cutoff_type)
MultiFunction._handlers_cache[algorithm_class] = cache_data
diff --git a/ufl/corealg/traversal.py b/ufl/corealg/traversal.py
index 782e16999..64c81deaa 100644
--- a/ufl/corealg/traversal.py
+++ b/ufl/corealg/traversal.py
@@ -39,7 +39,11 @@ def post_traversal(expr):
def cutoff_post_traversal(expr, cutofftypes):
- """Yield ``o`` for each node ``o`` in *expr*, child before parent, but skipping subtrees of the cutofftypes."""
+ """Cut-off post-tranversal.
+
+ Yield ``o`` for each node ``o`` in *expr*, child before parent, but
+ skipping subtrees of the cutofftypes.
+ """
lifo = [(expr, list(reversed(expr.ufl_operands)))]
while lifo:
expr, deps = lifo[-1]
diff --git a/ufl/differentiation.py b/ufl/differentiation.py
index 34c003b85..74e33617d 100644
--- a/ufl/differentiation.py
+++ b/ufl/differentiation.py
@@ -24,8 +24,7 @@
# --- Basic differentiation objects ---
-@ufl_type(is_abstract=True,
- is_differential=True)
+@ufl_type(is_abstract=True, is_differential=True)
class Derivative(Operator):
"""Base class for all derivative types."""
@@ -36,15 +35,13 @@ def __init__(self, operands):
Operator.__init__(self, operands)
-@ufl_type(num_ops=4, inherit_shape_from_operand=0,
- inherit_indices_from_operand=0)
+@ufl_type(num_ops=4, inherit_shape_from_operand=0, inherit_indices_from_operand=0)
class CoefficientDerivative(Derivative):
- """Derivative of the integrand of a form w.r.t. the degrees of freedom in a discrete Coefficient."""
+ """Derivative of form integrand w.r.t. the degrees of freedom in a discrete Coefficient."""
__slots__ = ()
- def __new__(cls, integrand, coefficients, arguments,
- coefficient_derivatives):
+ def __new__(cls, integrand, coefficients, arguments, coefficient_derivatives):
"""Create a new CoefficientDerivative."""
if not isinstance(coefficients, ExprList):
raise ValueError("Expecting ExprList instance with Coefficients.")
@@ -56,23 +53,23 @@ def __new__(cls, integrand, coefficients, arguments,
return integrand
return Derivative.__new__(cls)
- def __init__(self, integrand, coefficients, arguments,
- coefficient_derivatives):
+ def __init__(self, integrand, coefficients, arguments, coefficient_derivatives):
"""Initalise."""
if not isinstance(coefficient_derivatives, ExprMapping):
coefficient_derivatives = ExprMapping(coefficient_derivatives)
- Derivative.__init__(self, (integrand, coefficients, arguments,
- coefficient_derivatives))
+ Derivative.__init__(self, (integrand, coefficients, arguments, coefficient_derivatives))
def __str__(self):
"""Format as a string."""
- return "d/dfj { %s }, with fh=%s, dfh/dfj = %s, and coefficient derivatives %s"\
- % (self.ufl_operands[0], self.ufl_operands[1],
- self.ufl_operands[2], self.ufl_operands[3])
+ return "d/dfj { %s }, with fh=%s, dfh/dfj = %s, and coefficient derivatives %s" % (
+ self.ufl_operands[0],
+ self.ufl_operands[1],
+ self.ufl_operands[2],
+ self.ufl_operands[3],
+ )
-@ufl_type(num_ops=4, inherit_shape_from_operand=0,
- inherit_indices_from_operand=0)
+@ufl_type(num_ops=4, inherit_shape_from_operand=0, inherit_indices_from_operand=0)
class CoordinateDerivative(CoefficientDerivative):
"""Derivative of the integrand of a form w.r.t. the SpatialCoordinates."""
@@ -80,82 +77,92 @@ class CoordinateDerivative(CoefficientDerivative):
def __str__(self):
"""Format as a string."""
- return "d/dfj { %s }, with fh=%s, dfh/dfj = %s, and coordinate derivatives %s"\
- % (self.ufl_operands[0], self.ufl_operands[1],
- self.ufl_operands[2], self.ufl_operands[3])
+ return "d/dfj { %s }, with fh=%s, dfh/dfj = %s, and coordinate derivatives %s" % (
+ self.ufl_operands[0],
+ self.ufl_operands[1],
+ self.ufl_operands[2],
+ self.ufl_operands[3],
+ )
-@ufl_type(num_ops=4, inherit_shape_from_operand=0,
- inherit_indices_from_operand=0)
+@ufl_type(num_ops=4, inherit_shape_from_operand=0, inherit_indices_from_operand=0)
class BaseFormDerivative(CoefficientDerivative, BaseForm):
"""Derivative of a base form w.r.t the degrees of freedom in a discrete Coefficient."""
_ufl_noslots_ = True
- def __init__(self, base_form, coefficients, arguments,
- coefficient_derivatives):
+ def __init__(self, base_form, coefficients, arguments, coefficient_derivatives):
"""Initalise."""
- CoefficientDerivative.__init__(self, base_form, coefficients, arguments,
- coefficient_derivatives)
+ CoefficientDerivative.__init__(
+ self, base_form, coefficients, arguments, coefficient_derivatives
+ )
BaseForm.__init__(self)
def _analyze_form_arguments(self):
"""Collect the arguments of the corresponding BaseForm."""
from ufl.algorithms.analysis import extract_coefficients, extract_type
+
base_form, _, arguments, _ = self.ufl_operands
def arg_type(x):
if isinstance(x, BaseForm):
return Coargument
return Argument
+
# Each derivative arguments can either be a:
# - `ufl.BaseForm`: if it contains a `ufl.Coargument`
# - or a `ufl.Expr`: if it contains a `ufl.Argument`
- # When a `Coargument` is encountered, it is treated as an argument (i.e. as V* -> V* and not V* x V -> R)
- # and should result in one single argument (in the dual space).
- base_form_args = base_form.arguments() + tuple(arg for a in arguments.ufl_operands
- for arg in extract_type(a, arg_type(a)))
+ # When a `Coargument` is encountered, it is treated as an
+ # argument (i.e. as V* -> V* and not V* x V -> R) and should
+ # result in one single argument (in the dual space).
+ base_form_args = base_form.arguments() + tuple(
+ arg for a in arguments.ufl_operands for arg in extract_type(a, arg_type(a))
+ )
# BaseFormDerivative's arguments don't necessarily contain BaseArgument objects only
# -> e.g. `derivative(u ** 2, u, u)` with `u` a Coefficient.
- base_form_coeffs = base_form.coefficients() + tuple(arg for a in arguments.ufl_operands
- for arg in extract_coefficients(a))
+ base_form_coeffs = base_form.coefficients() + tuple(
+ arg for a in arguments.ufl_operands for arg in extract_coefficients(a)
+ )
# Reconstruct arguments for correct numbering
- self._arguments = tuple(type(arg)(arg.ufl_function_space(), arg.number(), arg.part()) for arg in base_form_args)
+ self._arguments = tuple(
+ type(arg)(arg.ufl_function_space(), arg.number(), arg.part()) for arg in base_form_args
+ )
self._coefficients = base_form_coeffs
-@ufl_type(num_ops=4, inherit_shape_from_operand=0,
- inherit_indices_from_operand=0)
+@ufl_type(num_ops=4, inherit_shape_from_operand=0, inherit_indices_from_operand=0)
class BaseFormCoordinateDerivative(BaseFormDerivative, CoordinateDerivative):
"""Derivative of a base form w.r.t. the SpatialCoordinates."""
_ufl_noslots_ = True
- def __init__(self, base_form, coefficients, arguments,
- coefficient_derivatives):
+ def __init__(self, base_form, coefficients, arguments, coefficient_derivatives):
"""Initalise."""
- BaseFormDerivative.__init__(self, base_form, coefficients, arguments,
- coefficient_derivatives)
+ BaseFormDerivative.__init__(
+ self, base_form, coefficients, arguments, coefficient_derivatives
+ )
-@ufl_type(num_ops=4, inherit_shape_from_operand=0,
- inherit_indices_from_operand=0)
+@ufl_type(num_ops=4, inherit_shape_from_operand=0, inherit_indices_from_operand=0)
class BaseFormOperatorDerivative(BaseFormDerivative, BaseFormOperator):
"""Derivative of a base form operator w.r.t the degrees of freedom in a discrete Coefficient."""
+
_ufl_noslots_ = True
# BaseFormOperatorDerivative is only needed because of a different
# differentiation procedure for BaseformOperator objects.
- def __init__(self, base_form, coefficients, arguments,
- coefficient_derivatives):
+ def __init__(self, base_form, coefficients, arguments, coefficient_derivatives):
"""Initalise."""
- BaseFormDerivative.__init__(self, base_form, coefficients, arguments,
- coefficient_derivatives)
+ BaseFormDerivative.__init__(
+ self, base_form, coefficients, arguments, coefficient_derivatives
+ )
self._argument_slots = base_form._argument_slots
- # Enforce Operator reconstruction as Operator is a parent class of both: BaseFormDerivative and BaseFormOperator.
- # Therfore the latter overwrites Operator reconstruction and we would have:
- # -> BaseFormOperatorDerivative._ufl_expr_reconstruct_ = BaseFormOperator._ufl_expr_reconstruct_
+ # Enforce Operator reconstruction as Operator is a parent class of
+ # both: BaseFormDerivative and BaseFormOperator.
+ # Therefore the latter overwrites Operator reconstruction and we would have:
+ # -> BaseFormOperatorDerivative._ufl_expr_reconstruct_ =
+ # BaseFormOperator._ufl_expr_reconstruct_
_ufl_expr_reconstruct_ = Operator._ufl_expr_reconstruct_
# Set __repr__
__repr__ = Operator.__repr__
@@ -163,23 +170,25 @@ def __init__(self, base_form, coefficients, arguments,
def argument_slots(self, outer_form=False):
"""Return a tuple of expressions containing argument and coefficient based expressions."""
from ufl.algorithms.analysis import extract_arguments
+
base_form, _, arguments, _ = self.ufl_operands
- argument_slots = (base_form.argument_slots(outer_form)
- + tuple(arg for a in arguments for arg in extract_arguments(a)))
+ argument_slots = base_form.argument_slots(outer_form) + tuple(
+ arg for a in arguments for arg in extract_arguments(a)
+ )
return argument_slots
-@ufl_type(num_ops=4, inherit_shape_from_operand=0,
- inherit_indices_from_operand=0)
+@ufl_type(num_ops=4, inherit_shape_from_operand=0, inherit_indices_from_operand=0)
class BaseFormOperatorCoordinateDerivative(BaseFormOperatorDerivative, CoordinateDerivative):
"""Derivative of a base form operator w.r.t. the SpatialCoordinates."""
+
_ufl_noslots_ = True
- def __init__(self, base_form, coefficients, arguments,
- coefficient_derivatives):
+ def __init__(self, base_form, coefficients, arguments, coefficient_derivatives):
"""Initalise."""
- BaseFormOperatorDerivative.__init__(self, base_form, coefficients, arguments,
- coefficient_derivatives)
+ BaseFormOperatorDerivative.__init__(
+ self, base_form, coefficients, arguments, coefficient_derivatives
+ )
@ufl_type(num_ops=2)
@@ -205,8 +214,7 @@ def __new__(cls, f, v):
# Simplification
# Return zero if expression is trivially independent of variable
if f._ufl_is_terminal_ and f != v:
- return Zero(f.ufl_shape + v.ufl_shape, f.ufl_free_indices,
- f.ufl_index_dimensions)
+ return Zero(f.ufl_shape + v.ufl_shape, f.ufl_free_indices, f.ufl_index_dimensions)
# Construction
return Derivative.__new__(cls)
@@ -222,12 +230,12 @@ def __str__(self):
"""Format as a string."""
if isinstance(self.ufl_operands[0], Terminal):
return "d%s/d[%s]" % (self.ufl_operands[0], self.ufl_operands[1])
- return "d/d[%s] %s" % (self.ufl_operands[1],
- parstr(self.ufl_operands[0], self))
+ return "d/d[%s] %s" % (self.ufl_operands[1], parstr(self.ufl_operands[0], self))
# --- Compound differentiation objects ---
+
@ufl_type(is_abstract=True)
class CompoundDerivative(Derivative):
"""Base class for all compound derivative types."""
@@ -250,8 +258,7 @@ def __new__(cls, f):
# Return zero if expression is trivially constant
if is_cellwise_constant(f):
dim = find_geometric_dimension(f)
- return Zero(f.ufl_shape + (dim,), f.ufl_free_indices,
- f.ufl_index_dimensions)
+ return Zero(f.ufl_shape + (dim,), f.ufl_free_indices, f.ufl_index_dimensions)
return CompoundDerivative.__new__(cls)
def __init__(self, f):
@@ -266,17 +273,16 @@ def _ufl_expr_reconstruct_(self, op):
raise ValueError("Operand shape mismatch in Grad reconstruct.")
if self.ufl_operands[0].ufl_free_indices != op.ufl_free_indices:
raise ValueError("Free index mismatch in Grad reconstruct.")
- return Zero(self.ufl_shape, self.ufl_free_indices,
- self.ufl_index_dimensions)
+ return Zero(self.ufl_shape, self.ufl_free_indices, self.ufl_index_dimensions)
return self._ufl_class_(op)
def evaluate(self, x, mapping, component, index_values, derivatives=()):
"""Get child from mapping and return the component asked for."""
component, i = component[:-1], component[-1]
derivatives = derivatives + (i,)
- result = self.ufl_operands[0].evaluate(x, mapping, component,
- index_values,
- derivatives=derivatives)
+ result = self.ufl_operands[0].evaluate(
+ x, mapping, component, index_values, derivatives=derivatives
+ )
return result
@property
@@ -289,20 +295,20 @@ def __str__(self):
return "grad(%s)" % self.ufl_operands[0]
-@ufl_type(num_ops=1, inherit_indices_from_operand=0, is_terminal_modifier=True,
- is_in_reference_frame=True)
+@ufl_type(
+ num_ops=1, inherit_indices_from_operand=0, is_terminal_modifier=True, is_in_reference_frame=True
+)
class ReferenceGrad(CompoundDerivative):
"""Reference grad."""
- __slots__ = ("_dim", )
+ __slots__ = ("_dim",)
def __new__(cls, f):
"""Create a new ReferenceGrad."""
# Return zero if expression is trivially constant
if is_cellwise_constant(f):
dim = extract_unique_domain(f).topological_dimension()
- return Zero(f.ufl_shape + (dim,), f.ufl_free_indices,
- f.ufl_index_dimensions)
+ return Zero(f.ufl_shape + (dim,), f.ufl_free_indices, f.ufl_index_dimensions)
return CompoundDerivative.__new__(cls)
def __init__(self, f):
@@ -317,17 +323,16 @@ def _ufl_expr_reconstruct_(self, op):
raise ValueError("Operand shape mismatch in ReferenceGrad reconstruct.")
if self.ufl_operands[0].ufl_free_indices != op.ufl_free_indices:
raise ValueError("Free index mismatch in ReferenceGrad reconstruct.")
- return Zero(self.ufl_shape, self.ufl_free_indices,
- self.ufl_index_dimensions)
+ return Zero(self.ufl_shape, self.ufl_free_indices, self.ufl_index_dimensions)
return self._ufl_class_(op)
def evaluate(self, x, mapping, component, index_values, derivatives=()):
"""Get child from mapping and return the component asked for."""
component, i = component[:-1], component[-1]
derivatives = derivatives + (i,)
- result = self.ufl_operands[0].evaluate(x, mapping, component,
- index_values,
- derivatives=derivatives)
+ result = self.ufl_operands[0].evaluate(
+ x, mapping, component, index_values, derivatives=derivatives
+ )
return result
@property
@@ -371,8 +376,9 @@ def __str__(self):
return "div(%s)" % self.ufl_operands[0]
-@ufl_type(num_ops=1, inherit_indices_from_operand=0, is_terminal_modifier=True,
- is_in_reference_frame=True)
+@ufl_type(
+ num_ops=1, inherit_indices_from_operand=0, is_terminal_modifier=True, is_in_reference_frame=True
+)
class ReferenceDiv(CompoundDerivative):
"""Reference divergence."""
@@ -414,8 +420,7 @@ def __new__(cls, f):
# Return zero if expression is trivially constant
if is_cellwise_constant(f):
dim = find_geometric_dimension(f)
- return Zero((dim,) + f.ufl_shape, f.ufl_free_indices,
- f.ufl_index_dimensions)
+ return Zero((dim,) + f.ufl_shape, f.ufl_free_indices, f.ufl_index_dimensions)
return CompoundDerivative.__new__(cls)
def __init__(self, f):
@@ -430,8 +435,7 @@ def _ufl_expr_reconstruct_(self, op):
raise ValueError("Operand shape mismatch in NablaGrad reconstruct.")
if self.ufl_operands[0].ufl_free_indices != op.ufl_free_indices:
raise ValueError("Free index mismatch in NablaGrad reconstruct.")
- return Zero(self.ufl_shape, self.ufl_free_indices,
- self.ufl_index_dimensions)
+ return Zero(self.ufl_shape, self.ufl_free_indices, self.ufl_index_dimensions)
return self._ufl_class_(op)
@property
@@ -509,8 +513,9 @@ def __str__(self):
return "curl(%s)" % self.ufl_operands[0]
-@ufl_type(num_ops=1, inherit_indices_from_operand=0,
- is_terminal_modifier=True, is_in_reference_frame=True)
+@ufl_type(
+ num_ops=1, inherit_indices_from_operand=0, is_terminal_modifier=True, is_in_reference_frame=True
+)
class ReferenceCurl(CompoundDerivative):
"""Reference curl."""
diff --git a/ufl/domain.py b/ufl/domain.py
index f438006d0..8f4ae5138 100644
--- a/ufl/domain.py
+++ b/ufl/domain.py
@@ -7,7 +7,6 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
import numbers
-import warnings
from ufl.cell import AbstractCell
from ufl.core.ufl_id import attach_ufl_id
@@ -20,15 +19,22 @@
class AbstractDomain(object):
- """Symbolic representation of a geometric domain with only a geometric and topological dimension."""
+ """Symbolic representation of a geometric domain.
+
+ Domain has only a geometric and a topological dimension.
+ """
def __init__(self, topological_dimension, geometric_dimension):
"""Initialise."""
# Validate dimensions
if not isinstance(geometric_dimension, numbers.Integral):
- raise ValueError(f"Expecting integer geometric dimension, not {geometric_dimension.__class__}")
+ raise ValueError(
+ f"Expecting integer geometric dimension, not {geometric_dimension.__class__}"
+ )
if not isinstance(topological_dimension, numbers.Integral):
- raise ValueError(f"Expecting integer topological dimension, not {topological_dimension.__class__}")
+ raise ValueError(
+ f"Expecting integer topological dimension, not {topological_dimension.__class__}"
+ )
if topological_dimension > geometric_dimension:
raise ValueError("Topological dimension cannot be larger than geometric dimension.")
@@ -67,6 +73,7 @@ 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, AbstractCell)):
raise ValueError("Expecting a coordinate element in the ufl.Mesh construct.")
@@ -74,7 +81,7 @@ def __init__(self, coordinate_element, ufl_id=None, cargo=None):
self._ufl_coordinate_element = coordinate_element
# Derive dimensions from element
- gdim, = coordinate_element.value_shape
+ (gdim,) = coordinate_element.reference_value_shape
tdim = coordinate_element.cell.topological_dimension()
AbstractDomain.__init__(self, tdim, gdim)
@@ -117,8 +124,7 @@ def _ufl_signature_data_(self, renumbering):
def _ufl_sort_key_(self):
"""UFL sort key."""
typespecific = (self._ufl_id, self._ufl_coordinate_element)
- return (self.geometric_dimension(), self.topological_dimension(),
- "Mesh", typespecific)
+ return (self.geometric_dimension(), self.topological_dimension(), "Mesh", typespecific)
@attach_ufl_id
@@ -134,7 +140,7 @@ def __init__(self, mesh, topological_dimension, ufl_id=None):
# Derive dimensions from element
coordinate_element = mesh.ufl_coordinate_element()
- gdim, = coordinate_element.value_shape
+ (gdim,) = coordinate_element.value_shape
tdim = coordinate_element.cell.topological_dimension()
AbstractDomain.__init__(self, tdim, gdim)
@@ -159,7 +165,10 @@ def __repr__(self):
def __str__(self):
"""Format as a string."""
return "" % (
- self._ufl_id, self.topological_dimension(), self._ufl_mesh)
+ self._ufl_id,
+ self.topological_dimension(),
+ self._ufl_mesh,
+ )
def _ufl_hash_data_(self):
"""UFL hash data."""
@@ -167,16 +176,14 @@ def _ufl_hash_data_(self):
def _ufl_signature_data_(self, renumbering):
"""UFL signature data."""
- return ("MeshView", renumbering[self],
- self._ufl_mesh._ufl_signature_data_(renumbering))
+ return ("MeshView", renumbering[self], self._ufl_mesh._ufl_signature_data_(renumbering))
# NB! Dropped __lt__ here, don't want users to write 'mesh1 <
# mesh2'.
def _ufl_sort_key_(self):
"""UFL sort key."""
typespecific = (self._ufl_id, self._ufl_mesh)
- return (self.geometric_dimension(), self.topological_dimension(),
- "MeshView", typespecific)
+ return (self.geometric_dimension(), self.topological_dimension(), "MeshView", typespecific)
def as_domain(domain):
@@ -212,42 +219,22 @@ def join_domains(domains):
gdims.add(domain.geometric_dimension())
if len(gdims) != 1:
raise ValueError("Found domains with different geometric dimensions.")
- gdim, = gdims
- # Split into legacy and modern style domains
- legacy_domains = []
- modern_domains = []
- for domain in domains:
- if isinstance(domain, Mesh) and domain.ufl_id() < 0:
- assert domain.ufl_cargo() is None
- legacy_domains.append(domain)
- else:
- modern_domains.append(domain)
-
- # Handle legacy domains checking
- if legacy_domains:
- warnings.warn("The use of Legacy domains will be deprecated by December 2023. "
- "Please, use FunctionSpace instead", DeprecationWarning)
- if modern_domains:
- raise ValueError(
- "Found both a new-style domain and a legacy default domain. "
- "These should not be used interchangeably. To find the legacy "
- "domain, note that it is automatically created from a cell so "
- "look for constructors taking a cell.")
- return tuple(legacy_domains)
-
- # Handle modern domains checking (assuming correct by construction)
- return tuple(modern_domains)
+ return domains
# TODO: Move these to an analysis module?
+
def extract_domains(expr):
"""Return all domains expression is defined on."""
domainlist = []
for t in traverse_unique_terminals(expr):
domainlist.extend(t.ufl_domains())
- return sorted(join_domains(domainlist))
+ return sorted(
+ join_domains(domainlist),
+ key=lambda D: (D.topological_dimension(), D.ufl_cell(), D.ufl_id()),
+ )
def extract_unique_domain(expr):
@@ -268,14 +255,8 @@ def find_geometric_dimension(expr):
domain = extract_unique_domain(t)
if domain is not None:
gdims.add(domain.geometric_dimension())
- if hasattr(t, "ufl_element"):
- element = t.ufl_element()
- if element is not None:
- cell = element.cell
- if cell is not None:
- gdims.add(cell.geometric_dimension())
if len(gdims) != 1:
raise ValueError("Cannot determine geometric dimension from expression.")
- gdim, = gdims
+ (gdim,) = gdims
return gdim
diff --git a/ufl/duals.py b/ufl/duals.py
index 1e017a547..22681440b 100644
--- a/ufl/duals.py
+++ b/ufl/duals.py
@@ -14,7 +14,7 @@ def is_primal(object):
because a mixed function space containing both primal
and dual components is neither primal nor dual.
"""
- return hasattr(object, '_primal') and object._primal
+ return hasattr(object, "_primal") and object._primal
def is_dual(object):
@@ -24,4 +24,4 @@ def is_dual(object):
because a mixed function space containing both primal
and dual components is neither primal nor dual.
"""
- return hasattr(object, '_dual') and object._dual
+ return hasattr(object, "_dual") and object._dual
diff --git a/ufl/equation.py b/ufl/equation.py
index f36089abb..956ae80a1 100644
--- a/ufl/equation.py
+++ b/ufl/equation.py
@@ -41,6 +41,7 @@ def __bool__(self):
return self.rhs.equals(self.lhs)
else:
raise ValueError("Either lhs or rhs of Equation must implement self.equals(other).")
+
__nonzero__ = __bool__
def __eq__(self, other):
diff --git a/ufl/exproperators.py b/ufl/exproperators.py
index 60eb87566..ff532e843 100644
--- a/ufl/exproperators.py
+++ b/ufl/exproperators.py
@@ -85,9 +85,13 @@ def _ne(self, other):
def _as_tensor(self, indices):
"""A^indices := as_tensor(A, indices)."""
if not isinstance(indices, tuple):
- raise ValueError("Expecting a tuple of Index objects to A^indices := as_tensor(A, indices).")
+ raise ValueError(
+ "Expecting a tuple of Index objects to A^indices := as_tensor(A, indices)."
+ )
if not all(isinstance(i, Index) for i in indices):
- raise ValueError("Expecting a tuple of Index objects to A^indices := as_tensor(A, indices).")
+ raise ValueError(
+ "Expecting a tuple of Index objects to A^indices := as_tensor(A, indices)."
+ )
return as_tensor(self, indices)
@@ -96,6 +100,7 @@ def _as_tensor(self, indices):
# --- Helper functions for product handling ---
+
def _mult(a, b):
"""Multiply."""
# Discover repeated indices, which results in index sums
@@ -307,6 +312,7 @@ def _abs(self):
# --- Extend Expr with restiction operators a("+"), a("-") ---
+
def _restrict(self, side):
"""Restrict."""
if side == "+":
@@ -324,6 +330,7 @@ def _eval(self, coord, mapping=None, component=()):
"""
# Evaluate derivatives first
from ufl.algorithms import expand_derivatives
+
f = expand_derivatives(self)
# Evaluate recursively
@@ -348,10 +355,12 @@ def _call(self, arg, mapping=None, component=()):
# --- Extend Expr with the transpose operation A.T ---
+
def _transpose(self):
"""Transpose a rank-2 tensor expression.
- For more general transpose operations of higher order tensor expressions, use indexing and Tensor.
+ For more general transpose operations of higher order tensor
+ expressions, use indexing and Tensor.
"""
return Transposed(self)
@@ -361,6 +370,7 @@ def _transpose(self):
# --- Extend Expr with indexing operator a[i] ---
+
def _getitem(self, component):
"""Get an item."""
# Treat component consistently as tuple below
@@ -370,12 +380,16 @@ def _getitem(self, component):
shape = self.ufl_shape
# Analyse slices (:) and Ellipsis (...)
- all_indices, slice_indices, repeated_indices = create_slice_indices(component, shape, self.ufl_free_indices)
+ all_indices, slice_indices, repeated_indices = create_slice_indices(
+ component, shape, self.ufl_free_indices
+ )
# Check that we have the right number of indices for a tensor with
# this shape
if len(shape) != len(all_indices):
- raise ValueError(f"Invalid number of indices {len(all_indices)} for expression of rank {len(shape)}.")
+ raise ValueError(
+ f"Invalid number of indices {len(all_indices)} for expression of rank {len(shape)}."
+ )
# Special case for simplifying foo[...] => foo, foo[:] => foo or
# similar
@@ -422,6 +436,7 @@ def _getitem(self, component):
# --- Extend Expr with spatial differentiation operator a.dx(i) ---
+
def _dx(self, *ii):
"""Return the partial derivative with respect to spatial variable number *ii*."""
d = self
diff --git a/ufl/finiteelement.py b/ufl/finiteelement.py
index 9a4e43d6e..8dbc76ed1 100644
--- a/ufl/finiteelement.py
+++ b/ufl/finiteelement.py
@@ -15,8 +15,6 @@
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
@@ -31,12 +29,14 @@
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.
+ 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
+ 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
"""
@@ -66,29 +66,33 @@ def pullback(self) -> _AbstractPullback:
@_abc.abstractproperty
def embedded_superdegree(self) -> _typing.Union[int, None]:
- """Return the degree of the minimum degree Lagrange space that spans this element.
+ """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.
+ 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
+ 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.
+ """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.
+ 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
+ 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.
"""
@@ -124,40 +128,6 @@ 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."""
@@ -175,15 +145,31 @@ def num_sub_elements(self) -> int:
class FiniteElement(AbstractFiniteElement):
"""A directly defined finite element."""
- __slots__ = ("_repr", "_str", "_family", "_cell", "_degree",
- "_reference_value_shape", "_pullback", "_sobolev_space",
- "_sub_elements", "_subdegree")
+
+ __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,
+ 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.
@@ -209,12 +195,14 @@ def __init__(
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})")
+ 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})")
+ f'ufl.finiteelement.FiniteElement("{family}", {cell}, {degree}, '
+ f"{reference_value_shape}, {pullback}, {sobolev_space})"
+ )
else:
self._repr = _repr
if _str is None:
@@ -257,30 +245,34 @@ def pullback(self) -> _AbstractPullback:
@property
def embedded_superdegree(self) -> _typing.Union[int, None]:
- """Return the degree of the minimum degree Lagrange space that spans this element.
+ """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.
+ 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
+ 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.
+ """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.
+ 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
+ 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
@@ -299,8 +291,8 @@ def reference_value_shape(self) -> _typing.Tuple[int, ...]:
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.
+ This function does not recurse: ie it does not extract the
+ sub-elements of sub-elements.
"""
return self._sub_elements
@@ -311,7 +303,7 @@ class SymmetricElement(FiniteElement):
def __init__(
self,
symmetry: _typing.Dict[_typing.Tuple[int, ...], int],
- sub_elements: _typing.List[AbstractFiniteElement]
+ sub_elements: _typing.List[AbstractFiniteElement],
):
"""Initialise a symmetric element.
@@ -323,7 +315,7 @@ def __init__(
"""
self._sub_elements = sub_elements
pullback = _SymmetricPullback(self, symmetry)
- reference_value_shape = (sum(e.reference_value_size for e in sub_elements), )
+ 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:
@@ -332,10 +324,16 @@ def __init__(
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,
+ "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"")
+ _str=f"",
+ )
class MixedElement(FiniteElement):
@@ -354,7 +352,7 @@ def __init__(self, sub_elements):
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), )
+ 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:
@@ -362,7 +360,13 @@ def __init__(self, sub_elements):
sobolev_space = max(e.sobolev_space for e in sub_elements)
super().__init__(
- "Mixed element", cell, degree, reference_value_shape, pullback, sobolev_space,
+ "Mixed element",
+ cell,
+ degree,
+ reference_value_shape,
+ pullback,
+ sobolev_space,
sub_elements=sub_elements,
_repr=f"ufl.finiteelement.MixedElement({sub_elements!r})",
- _str=f"")
+ _str=f"",
+ )
diff --git a/ufl/form.py b/ufl/form.py
index 3f85b709d..4fc7c2165 100644
--- a/ufl/form.py
+++ b/ufl/form.py
@@ -34,15 +34,20 @@
def _sorted_integrals(integrals):
- """Sort integrals by domain id, integral type, subdomain id for a more stable signature computation."""
+ """Sort integrals for a stable signature computation.
+
+ Sort integrals by domain id, integral type, subdomain id for a more
+ stable signature computation.
+ """
# Group integrals in multilevel dict by keys
# [domain][integral_type][subdomain_id]
- integrals_dict = defaultdict(
- lambda: defaultdict(lambda: defaultdict(list)))
+ integrals_dict = defaultdict(lambda: defaultdict(lambda: defaultdict(list)))
for integral in integrals:
d = integral.ufl_domain()
if d is None:
- raise ValueError("Each integral in a form must have a uniquely defined integration domain.")
+ raise ValueError(
+ "Each integral in a form must have a uniquely defined integration domain."
+ )
it = integral.integral_type()
si = integral.subdomain_id()
integrals_dict[d][it][si] += [integral]
@@ -82,7 +87,7 @@ class BaseForm(object, metaclass=UFLType):
# classes
__slots__ = ()
_ufl_is_abstract_ = True
- _ufl_required_methods_ = ('_analyze_form_arguments', '_analyze_domains', "ufl_domains")
+ _ufl_required_methods_ = ("_analyze_form_arguments", "_analyze_domains", "ufl_domains")
def __init__(self):
"""Initialise."""
@@ -108,13 +113,12 @@ def ufl_domain(self):
Fails if multiple domains are found.
"""
- if self._domains is None:
- self._analyze_domains()
-
- if len(self._domains) > 1:
+ try:
+ (domain,) = set(self.ufl_domains())
+ except ValueError:
raise ValueError("%s must have exactly one domain." % type(self).__name__)
- # Return the single geometric domain
- return self._domains[0]
+ # Return the one and only domain
+ return domain
# --- Operator implementations ---
@@ -134,10 +138,10 @@ def __radd__(self, other):
def __add__(self, other):
"""Add."""
- if isinstance(other, (int, float)) and other == 0:
+ if isinstance(other, numbers.Number) and other == 0:
# Allow adding 0 or 0.0 as a no-op, needed for sum([a,b])
return self
- elif isinstance(other, Zero) and not (other.ufl_shape or other.ufl_free_indices):
+ elif isinstance(other, Zero):
# Allow adding ufl Zero as a no-op, needed for sum([a,b])
return self
@@ -191,6 +195,7 @@ def __mul__(self, coefficient):
"""Take the action of this form on the given coefficient."""
if isinstance(coefficient, Expr):
from ufl.formoperators import action
+
return action(self, coefficient)
return NotImplemented
@@ -201,6 +206,7 @@ def __ne__(self, other):
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):
@@ -221,7 +227,8 @@ class Form(BaseForm):
"""Description of a weak form consisting of a sum of integrals over subdomains."""
__slots__ = (
- # --- List of Integral objects (a Form is a sum of these Integrals, everything else is derived)
+ # --- List of Integral objects (a Form is a sum of these
+ # Integrals, everything else is derived)
"_integrals",
# --- Internal variables for caching various data
"_integration_domains",
@@ -271,6 +278,7 @@ def __init__(self, integrals):
self._base_form_operators = None
from ufl.algorithms.analysis import extract_constants
+
self._constants = extract_constants(self)
# Internal variables for caching of hash and signature after
@@ -289,8 +297,9 @@ def integrals(self):
def integrals_by_type(self, integral_type):
"""Return a sequence of all integrals with a particular domain type."""
- return tuple(integral for integral in self.integrals()
- if integral.integral_type() == integral_type)
+ return tuple(
+ integral for integral in self.integrals() if integral.integral_type() == integral_type
+ )
def integrals_by_domain(self, domain):
"""Return a sequence of all integrals with a particular integration domain."""
@@ -303,7 +312,8 @@ def empty(self):
def ufl_domains(self):
"""Return the geometric integration domains occuring in the form.
- NB! This does not include domains of coefficients defined on other meshes.
+ NB! This does not include domains of coefficients defined on
+ other meshes.
The return type is a tuple even if only a single domain exists.
"""
@@ -318,32 +328,14 @@ def ufl_cell(self):
"""
return self.ufl_domain().ufl_cell()
- def ufl_domain(self):
- """Return the single geometric integration domain occuring in the form.
-
- Fails if multiple domains are found.
-
- NB! This does not include domains of coefficients defined on
- other meshes, look at form data for that additional
- information.
- """
- # Collect all domains
- domains = self.ufl_domains()
- # Check that all are equal TODO: don't return more than one if
- # all are equal?
- if not all(domain == domains[0] for domain in domains):
- raise ValueError("Calling Form.ufl_domain() is only valid if all integrals share domain.")
-
- # Return the one and only domain
- return domains[0]
-
def geometric_dimension(self):
"""Return the geometric dimension shared by all domains and functions in this form."""
- gdims = tuple(
- set(domain.geometric_dimension() for domain in self.ufl_domains()))
+ gdims = tuple(set(domain.geometric_dimension() for domain in self.ufl_domains()))
if len(gdims) != 1:
- raise ValueError("Expecting all domains and functions in a form "
- f"to share geometric dimension, got {tuple(sorted(gdims))}")
+ raise ValueError(
+ "Expecting all domains and functions in a form "
+ f"to share geometric dimension, got {tuple(sorted(gdims))}"
+ )
return gdims[0]
def domain_numbering(self):
@@ -358,12 +350,6 @@ def subdomain_data(self):
self._analyze_subdomain_data()
return self._subdomain_data
- def max_subdomain_ids(self):
- """Returns a mapping on the form ``{domain:{integral_type:max_subdomain_id}}``."""
- if self._max_subdomain_ids is None:
- self._analyze_subdomain_data()
- return self._max_subdomain_ids
-
def coefficients(self):
"""Return all ``Coefficient`` objects found in form."""
if self._coefficients is None:
@@ -477,9 +463,7 @@ def __add__(self, other):
# Allow adding 0 or 0.0 as a no-op, needed for sum([a,b])
return self
- elif isinstance(
- other,
- Zero) and not (other.ufl_shape or other.ufl_free_indices):
+ elif isinstance(other, Zero) and not (other.ufl_shape or other.ufl_free_indices):
# Allow adding ufl Zero as a no-op, needed for sum([a,b])
return self
@@ -514,6 +498,7 @@ def __mul__(self, coefficient):
"""UFL form operator: Take the action of this form on the given coefficient."""
if isinstance(coefficient, Expr):
from ufl.formoperators import action
+
return action(self, coefficient)
return NotImplemented
@@ -559,6 +544,7 @@ def __call__(self, *args, **kwargs):
warnings.warn("Coefficient %s is not in form." % ufl_err_str(f))
if repdict:
from ufl.formoperators import replace
+
return replace(self, repdict)
else:
return self
@@ -570,16 +556,18 @@ def __call__(self, *args, **kwargs):
def __str__(self):
"""Compute shorter string representation of form. This can be huge for complicated forms."""
# Warning used for making sure we don't use this in the general pipeline:
- # warning("Calling str on form is potentially expensive and should be avoided except during debugging.")
- # Not caching this because it can be huge
+ # warning("Calling str on form is potentially expensive and
+ # should be avoided except during debugging.") Not caching this
+ # because it can be huge
s = "\n + ".join(str(itg) for itg in self.integrals())
return s or ""
def __repr__(self):
"""Compute repr string of form. This can be huge for complicated forms."""
# Warning used for making sure we don't use this in the general pipeline:
- # warning("Calling repr on form is potentially expensive and should be avoided except during debugging.")
- # Not caching this because it can be huge
+ # warning("Calling repr on form is potentially expensive and
+ # should be avoided except during debugging.") Not caching this
+ # because it can be huge
itgs = ", ".join(repr(itg) for itg in self.integrals())
r = "Form([" + itgs + "])"
return r
@@ -626,17 +614,17 @@ def _analyze_subdomain_data(self):
def _analyze_form_arguments(self):
"""Analyze which Argument and Coefficient objects can be found in the form."""
from ufl.algorithms.analysis import extract_arguments_and_coefficients
+
arguments, coefficients = extract_arguments_and_coefficients(self)
# Define canonical numbering of arguments and coefficients
- self._arguments = tuple(
- sorted(set(arguments), key=lambda x: x.number()))
- self._coefficients = tuple(
- sorted(set(coefficients), key=lambda x: x.count()))
+ self._arguments = tuple(sorted(set(arguments), key=lambda x: x.number()))
+ self._coefficients = tuple(sorted(set(coefficients), key=lambda x: x.count()))
def _analyze_base_form_operators(self):
"""Analyze which BaseFormOperator objects can be found in the form."""
from ufl.algorithms.analysis import extract_base_form_operators
+
base_form_ops = extract_base_form_operators(self)
self._base_form_operators = tuple(sorted(base_form_ops, key=lambda x: x.count()))
@@ -679,6 +667,7 @@ def _compute_renumbering(self):
def _compute_signature(self):
"""Compute signature."""
from ufl.algorithms.signature import compute_form_signature
+
self._signature = compute_form_signature(self, self._compute_renumbering())
@@ -698,24 +687,28 @@ class FormSum(BaseForm):
arg_weights is a list of tuples of component index and weight
"""
- __slots__ = ("_arguments",
- "_coefficients",
- "_weights",
- "_components",
- "ufl_operands",
- "_domains",
- "_domain_numbering",
- "_hash")
- _ufl_required_methods_ = ('_analyze_form_arguments')
+ __slots__ = (
+ "_arguments",
+ "_coefficients",
+ "_weights",
+ "_components",
+ "ufl_operands",
+ "_domains",
+ "_domain_numbering",
+ "_hash",
+ )
+ _ufl_required_methods_ = "_analyze_form_arguments"
def __new__(cls, *args, **kwargs):
"""Create a new FormSum."""
# All the components are `ZeroBaseForm`
if all(component == 0 for component, _ in args):
- # Assume that the arguments of all the components have consistent with each other and select
- # the first one to define the arguments of `ZeroBaseForm`.
- # This might not always be true but `ZeroBaseForm`'s arguments are not checked anywhere
- # because we can't reliably always infer them.
+ # Assume that the arguments of all the components have
+ # consistent with each other and select the first one to
+ # define the arguments of `ZeroBaseForm`.
+ # This might not always be true but `ZeroBaseForm`'s
+ # arguments are not checked anywhere because we can't
+ # reliably always infer them.
((arg, _), *_) = args
arguments = arg.arguments()
return ZeroBaseForm(arguments)
@@ -731,7 +724,7 @@ def __init__(self, *components):
weights = []
full_components = []
- for (component, w) in filtered_components:
+ for component, w in filtered_components:
if isinstance(component, FormSum):
full_components.extend(component.components())
weights.extend([w * wc for wc in component.weights()])
@@ -762,7 +755,7 @@ def _sum_variational_components(self):
var_forms = None
other_components = []
new_weights = []
- for (i, component) in enumerate(self._components):
+ for i, component in enumerate(self._components):
if isinstance(component, Form):
if var_forms:
var_forms = var_forms + (self._weights[i] * component)
@@ -785,17 +778,23 @@ def _analyze_form_arguments(self):
arguments.extend(component.arguments())
coefficients.extend(component.coefficients())
# Define canonical numbering of arguments and coefficients
- self._arguments = tuple(
- sorted(set(arguments), key=lambda x: x.number()))
- self._coefficients = tuple(
- sorted(set(coefficients), key=lambda x: x.count()))
+ self._arguments = tuple(sorted(set(arguments), key=lambda x: x.number()))
+ self._coefficients = tuple(sorted(set(coefficients), key=lambda x: x.count()))
def _analyze_domains(self):
"""Analyze which domains can be found in FormSum."""
- from ufl.domain import join_domains
+ from ufl.domain import join_domains, sort_domains
# Collect unique domains
- self._domains = join_domains([component.ufl_domain() for component in self._components])
+ self._domains = sort_domains(
+ join_domains(chain.from_iterable(e.ufl_domains() for e in self.ufl_operands))
+ )
+
+ def ufl_domains(self):
+ """Return all domains found in the base form."""
+ if self._domains is None:
+ self._analyze_domains()
+ return self._domains
def __hash__(self):
"""Hash."""
@@ -809,13 +808,15 @@ def equals(self, other):
return False
if self is other:
return True
- return (len(self.components()) == len(other.components()) and # noqa: W504
- all(a == b for a, b in zip(self.components(), other.components())))
+ return len(self.components()) == len(other.components()) and all(
+ a == b for a, b in zip(self.components(), other.components())
+ )
def __str__(self):
"""Compute shorter string representation of form. This can be huge for complicated forms."""
# Warning used for making sure we don't use this in the general pipeline:
- # warning("Calling str on form is potentially expensive and should be avoided except during debugging.")
+ # warning("Calling str on form is potentially expensive and
+ # should be avoided except during debugging.")
# Not caching this because it can be huge
s = "\n + ".join(str(component) for component in self.components())
return s or ""
@@ -823,7 +824,8 @@ def __str__(self):
def __repr__(self):
"""Compute repr string of form. This can be huge for complicated forms."""
# Warning used for making sure we don't use this in the general pipeline:
- # warning("Calling repr on form is potentially expensive and should be avoided except during debugging.")
+ # warning("Calling repr on form is potentially expensive and
+ # should be avoided except during debugging.")
# Not caching this because it can be huge
itgs = ", ".join(repr(component) for component in self.components())
r = "FormSum([" + itgs + "])"
@@ -838,12 +840,15 @@ class ZeroBaseForm(BaseForm):
used for sake of simplifying base-form expressions.
"""
- __slots__ = ("_arguments",
- "_coefficients",
- "ufl_operands",
- "_hash",
- # Pyadjoint compatibility
- "form")
+ __slots__ = (
+ "_arguments",
+ "_coefficients",
+ "ufl_operands",
+ "_domains",
+ "_hash",
+ # Pyadjoint compatibility
+ "form",
+ )
def __init__(self, arguments):
"""Initialise."""
@@ -858,6 +863,21 @@ def _analyze_form_arguments(self):
# `self._arguments` is already set in `BaseForm.__init__`
self._coefficients = ()
+ def _analyze_domains(self):
+ """Analyze which domains can be found in ZeroBaseForm."""
+ from ufl.domain import join_domains, sort_domains
+
+ # Collect unique domains
+ self._domains = sort_domains(
+ join_domains(chain.from_iterable(e.ufl_domains() for e in self.ufl_operands))
+ )
+
+ def ufl_domains(self):
+ """Return all domains found in the base form."""
+ if self._domains is None:
+ self._analyze_domains()
+ return self._domains
+
def __ne__(self, other):
"""Overwrite BaseForm.__neq__ which relies on `equals`."""
return not self == other
@@ -867,7 +887,7 @@ def __eq__(self, other):
if type(other) is ZeroBaseForm:
if self is other:
return True
- return (self._arguments == other._arguments)
+ return self._arguments == other._arguments
elif isinstance(other, (int, float)):
return other == 0
else:
diff --git a/ufl/formatting/ufl2unicode.py b/ufl/formatting/ufl2unicode.py
index 5c193da40..5d8d5f0c5 100644
--- a/ufl/formatting/ufl2unicode.py
+++ b/ufl/formatting/ufl2unicode.py
@@ -11,6 +11,7 @@
try:
import colorama
+
has_colorama = True
except ImportError:
has_colorama = False
@@ -26,6 +27,7 @@ def __init__(self):
def highest(self, o):
"""Return the highest precendence."""
return 0
+
terminal = highest
list_tensor = highest
component_tensor = highest
@@ -33,12 +35,14 @@ def highest(self, o):
def restricted(self, o):
"""Return precedence of a restriced."""
return 5
+
cell_avg = restricted
facet_avg = restricted
def call(self, o):
"""Return precedence of a call."""
return 10
+
indexed = call
min_value = call
max_value = call
@@ -52,6 +56,7 @@ def power(self, o):
def mathop(self, o):
"""Return precedence of a mathop."""
return 15
+
derivative = mathop
trace = mathop
deviatoric = mathop
@@ -66,6 +71,7 @@ def not_condition(self, o):
def product(self, o):
"""Return precedence of a product."""
return 30
+
division = product
# mod = product
dot = product
@@ -76,12 +82,14 @@ def product(self, o):
def add(self, o):
"""Return precedence of an add."""
return 40
+
# sub = add
index_sum = add
def lt(self, o):
"""Return precedence of a lt."""
return 50
+
le = lt
gt = lt
ge = lt
@@ -89,6 +97,7 @@ def lt(self, o):
def eq(self, o):
"""Return precedence of an eq."""
return 60
+
ne = eq
def and_condition(self, o):
@@ -106,6 +115,7 @@ def conditional(self, o):
def lowest(self, o):
"""Return precedence of a lowest."""
return 80
+
operator = lowest
@@ -121,81 +131,81 @@ class UC:
"""An enum-like class for unicode characters."""
# Letters in this alphabet have contiguous code point numbers
- bold_math_a = u"𝐚"
- bold_math_A = u"𝐀"
+ bold_math_a = "𝐚"
+ bold_math_A = "𝐀"
- thin_space = u"\u2009"
+ thin_space = "\u2009"
- superscript_plus = u"⁺"
- superscript_minus = u"⁻"
- superscript_equals = u"⁼"
- superscript_left_paren = u"⁽"
- superscript_right_paren = u"⁾"
+ superscript_plus = "⁺"
+ superscript_minus = "⁻"
+ superscript_equals = "⁼"
+ superscript_left_paren = "⁽"
+ superscript_right_paren = "⁾"
superscript_digits = ["⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹"]
- subscript_plus = u"₊"
- subscript_minus = u"₋"
- subscript_equals = u"₌"
- subscript_left_paren = u"₍"
- subscript_right_paren = u"₎"
+ subscript_plus = "₊"
+ subscript_minus = "₋"
+ subscript_equals = "₌"
+ subscript_left_paren = "₍"
+ subscript_right_paren = "₎"
subscript_digits = ["₀", "₁", "₂", "₃", "₄", "₅", "₆", "₇", "₈", "₉"]
- sqrt = u"√"
- transpose = u"ᵀ"
+ sqrt = "√"
+ transpose = "ᵀ"
- integral = u"∫"
- integral_double = u"∬"
- integral_triple = u"∭"
- integral_contour = u"∮"
- integral_surface = u"∯"
- integral_volume = u"∰"
+ integral = "∫"
+ integral_double = "∬"
+ integral_triple = "∭"
+ integral_contour = "∮"
+ integral_surface = "∯"
+ integral_volume = "∰"
- sum = u"∑"
+ sum = "∑"
division_slash = "∕"
- partial = u"∂"
- epsilon = u"ε"
- omega = u"ω"
- Omega = u"Ω"
- gamma = u"γ"
- Gamma = u"Γ"
- nabla = u"∇"
- for_all = u"∀"
-
- dot = u"⋅"
- cross_product = u"⨯"
- circled_times = u"⊗"
- nary_product = u"∏"
-
- ne = u"≠"
- lt = u"<"
- le = u"≤"
- gt = u">"
- ge = u"≥"
-
- logical_and = u"∧"
- logical_or = u"∨"
- logical_not = u"¬"
-
- element_of = u"∈"
- not_element_of = u"∉"
-
- left_white_square_bracket = u"⟦"
- right_white_squared_bracket = u"⟧"
- left_angled_bracket = u"⟨"
- right_angled_bracket = u"⟩"
- left_double_angled_bracket = u"⟪"
- right_double_angled_bracket = u"⟫"
-
- combining_right_arrow_above = "\u20D7"
+ partial = "∂"
+ epsilon = "ε"
+ omega = "ω"
+ Omega = "Ω"
+ gamma = "γ"
+ Gamma = "Γ"
+ nabla = "∇"
+ for_all = "∀"
+
+ dot = "⋅"
+ cross_product = "⨯"
+ circled_times = "⊗"
+ nary_product = "∏"
+
+ ne = "≠"
+ lt = "<"
+ le = "≤"
+ gt = ">"
+ ge = "≥"
+
+ logical_and = "∧"
+ logical_or = "∨"
+ logical_not = "¬"
+
+ element_of = "∈"
+ not_element_of = "∉"
+
+ left_white_square_bracket = "⟦"
+ right_white_squared_bracket = "⟧"
+ left_angled_bracket = "⟨"
+ right_angled_bracket = "⟩"
+ left_double_angled_bracket = "⟪"
+ right_double_angled_bracket = "⟫"
+
+ combining_right_arrow_above = "\u20d7"
combining_overline = "\u0305"
def bolden_letter(c):
"""Bolden a letter."""
if ord("A") <= ord(c) <= ord("Z"):
- c = chr(ord(c) - ord(u"A") + ord(UC.bold_math_A))
+ c = chr(ord(c) - ord("A") + ord(UC.bold_math_A))
elif ord("a") <= ord(c) <= ord("z"):
- c = chr(ord(c) - ord(u"a") + ord(UC.bold_math_a))
+ c = chr(ord(c) - ord("a") + ord(UC.bold_math_a))
return c
@@ -211,12 +221,12 @@ def subscript_digit(digit):
def bolden_string(s):
"""Bolden a string."""
- return u"".join(bolden_letter(c) for c in s)
+ return "".join(bolden_letter(c) for c in s)
def overline_string(f):
"""Overline a string."""
- return u"".join(f"{c}{UC.combining_overline}" for c in f)
+ return "".join(f"{c}{UC.combining_overline}" for c in f)
def subscript_number(number):
@@ -245,12 +255,7 @@ def measure_font(dx):
return bolden_string(dx)
-integral_by_dim = {
- 3: UC.integral_triple,
- 2: UC.integral_double,
- 1: UC.integral,
- 0: UC.integral
-}
+integral_by_dim = {3: UC.integral_triple, 2: UC.integral_double, 1: UC.integral, 0: UC.integral}
integral_type_to_codim = {
"cell": 0,
@@ -366,8 +371,7 @@ def form2unicode(form, formdata):
lines = []
integrals = form.integrals()
for itg in integrals:
- integrand_string = expression2unicode(
- itg.integrand(), argument_names, coefficient_names)
+ integrand_string = expression2unicode(itg.integrand(), argument_names, coefficient_names)
istr, dxstr = get_integral_symbol(itg.integral_type(), itg.ufl_domain(), itg.subdomain_id())
@@ -799,7 +803,7 @@ def conditional(self, o, c, t, f):
f = par(t)
If = opfont("if")
Else = opfont("else")
- return " ".join((t, If, c, Else, f))
+ return f"{t} {If} {c} {Else} {f}"
def min_value(self, o, a, b):
"""Format an min_value."""
diff --git a/ufl/formoperators.py b/ufl/formoperators.py
index d5cc31117..6616c7fff 100644
--- a/ufl/formoperators.py
+++ b/ufl/formoperators.py
@@ -11,17 +11,32 @@
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.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,
+ replace, # noqa: F401
+)
from ufl.argument import Argument
from ufl.coefficient import Coefficient, Cofunction
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.differentiation import (BaseFormCoordinateDerivative, BaseFormDerivative, BaseFormOperatorCoordinateDerivative,
- BaseFormOperatorDerivative, CoefficientDerivative, CoordinateDerivative)
+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
@@ -107,8 +122,9 @@ def action(form, coefficient=None, derivatives_expanded=None):
become expensive -> `derivatives_expanded` enables to use caching mechanisms to avoid that.
"""
form = as_form(form)
- is_coefficient_valid = (not isinstance(coefficient, BaseForm) or
- (isinstance(coefficient, BaseFormOperator) and len(coefficient.arguments()) == 1))
+ is_coefficient_valid = not isinstance(coefficient, BaseForm) or (
+ isinstance(coefficient, BaseFormOperator) and len(coefficient.arguments()) == 1
+ )
# Can't expand derivatives on objects that are not Form or Expr (e.g. Matrix)
if isinstance(form, (Form, BaseFormOperator)) and is_coefficient_valid:
if not derivatives_expanded:
@@ -191,8 +207,12 @@ def _handle_derivative_arguments(form, coefficient, argument):
if argument is None:
# Try to create argument if not provided
- if not all(isinstance(c, (Coefficient, Cofunction, BaseFormOperator)) for c in coefficients):
- raise ValueError("Can only create arguments automatically for non-indexed coefficients.")
+ if not all(
+ isinstance(c, (Coefficient, Cofunction, BaseFormOperator)) for c in coefficients
+ ):
+ raise ValueError(
+ "Can only create arguments automatically for non-indexed coefficients."
+ )
# Get existing arguments from form and position the new one
# with the next argument number
@@ -241,7 +261,7 @@ def _handle_derivative_arguments(form, coefficient, argument):
# Build mapping from coefficient to argument
m = {}
- for (c, a) in zip(coefficients, arguments):
+ for c, a in zip(coefficients, arguments):
if c.ufl_shape != a.ufl_shape:
raise ValueError("Coefficient and argument shapes do not match!")
if isinstance(c, (Coefficient, Cofunction, BaseFormOperator, SpatialCoordinate)):
@@ -297,12 +317,16 @@ def derivative(form, coefficient, argument=None, coefficient_derivatives=None):
"""
if isinstance(form, FormSum):
# Distribute derivative over FormSum components
- return FormSum(*[(derivative(component, coefficient, argument, coefficient_derivatives), 1)
- for component in form.components()])
+ return FormSum(
+ *[
+ (derivative(component, coefficient, argument, coefficient_derivatives), 1)
+ for component in form.components()
+ ]
+ )
elif isinstance(form, Adjoint):
# Is `derivative(Adjoint(A), ...)` with A a 2-form even legal ?
# -> If yes, what's the right thing to do here ?
- raise NotImplementedError('Adjoint derivative is not supported.')
+ raise NotImplementedError("Adjoint derivative is not supported.")
elif isinstance(form, Action):
# Push derivative through Action slots
left, right = form.ufl_operands
@@ -314,13 +338,15 @@ def derivative(form, coefficient, argument=None, coefficient_derivatives=None):
dleft = derivative(left, coefficient, argument, coefficient_derivatives)
dright = derivative(right, coefficient, argument, coefficient_derivatives)
# Leibniz formula
- return (action(adjoint(dleft, derivatives_expanded=True), right, derivatives_expanded=True)
- + action(left, dright, derivatives_expanded=True))
+ return action(
+ adjoint(dleft, derivatives_expanded=True), right, derivatives_expanded=True
+ ) + action(left, dright, derivatives_expanded=True)
else:
- raise NotImplementedError('Action derivative not supported when the left argument is not a 1-form.')
+ raise NotImplementedError(
+ "Action derivative not supported when the left argument is not a 1-form."
+ )
- coefficients, arguments = _handle_derivative_arguments(form, coefficient,
- argument)
+ coefficients, arguments = _handle_derivative_arguments(form, coefficient, argument)
if coefficient_derivatives is None:
coefficient_derivatives = ExprMapping()
else:
@@ -334,38 +360,46 @@ def derivative(form, coefficient, argument=None, coefficient_derivatives=None):
integrals = []
for itg in form.integrals():
if isinstance(coefficient, SpatialCoordinate):
- fd = CoordinateDerivative(itg.integrand(), coefficients,
- arguments, coefficient_derivatives)
- elif isinstance(coefficient, BaseForm) and not isinstance(coefficient, BaseFormOperator):
+ fd = CoordinateDerivative(
+ itg.integrand(), coefficients, arguments, coefficient_derivatives
+ )
+ elif isinstance(coefficient, BaseForm) and not isinstance(
+ coefficient, BaseFormOperator
+ ):
# Make the `ZeroBaseForm` arguments
arguments = form.arguments() + coefficient.arguments()
return ZeroBaseForm(arguments)
else:
- fd = CoefficientDerivative(itg.integrand(), coefficients,
- arguments, coefficient_derivatives)
+ fd = CoefficientDerivative(
+ itg.integrand(), coefficients, arguments, coefficient_derivatives
+ )
integrals.append(itg.reconstruct(fd))
return Form(integrals)
elif isinstance(form, BaseFormOperator):
if not isinstance(coefficient, SpatialCoordinate):
- return BaseFormOperatorDerivative(form, coefficients, arguments, coefficient_derivatives)
+ return BaseFormOperatorDerivative(
+ form, coefficients, arguments, coefficient_derivatives
+ )
else:
- return BaseFormOperatorCoordinateDerivative(form, coefficients, arguments, coefficient_derivatives)
+ return BaseFormOperatorCoordinateDerivative(
+ form, coefficients, arguments, coefficient_derivatives
+ )
elif isinstance(form, BaseForm):
if not isinstance(coefficient, SpatialCoordinate):
return BaseFormDerivative(form, coefficients, arguments, coefficient_derivatives)
else:
- return BaseFormCoordinateDerivative(form, coefficients, arguments, coefficient_derivatives)
+ return BaseFormCoordinateDerivative(
+ form, coefficients, arguments, coefficient_derivatives
+ )
elif isinstance(form, Expr):
# What we got was in fact an integrand
if not isinstance(coefficient, SpatialCoordinate):
- return CoefficientDerivative(form, coefficients,
- arguments, coefficient_derivatives)
+ return CoefficientDerivative(form, coefficients, arguments, coefficient_derivatives)
else:
- return CoordinateDerivative(form, coefficients,
- arguments, coefficient_derivatives)
+ return CoordinateDerivative(form, coefficients, arguments, coefficient_derivatives)
raise ValueError(f"Invalid argument type {type(form)}.")
@@ -397,8 +431,8 @@ def sensitivity_rhs(a, u, L, v):
::
v = variable(v_expression)
- L = IL(v)*dx
- a = Ia(v)*dx
+ L = IL(v) * dx
+ a = Ia(v) * dx
where ``IL`` and ``Ia`` are integrand expressions.
Define a ``Coefficient u`` representing the solution
@@ -425,9 +459,17 @@ def sensitivity_rhs(a, u, L, v):
dL = sensitivity_rhs(a, u, L, v)
"""
- if not (isinstance(a, Form) and isinstance(u, Coefficient) and isinstance(L, Form) and isinstance(v, Variable)):
- raise ValueError("Expecting (a, u, L, v), (bilinear form, function, linear form and scalar variable).")
+ if not (
+ isinstance(a, Form)
+ and isinstance(u, Coefficient)
+ and isinstance(L, Form)
+ and isinstance(v, Variable)
+ ):
+ raise ValueError(
+ "Expecting (a, u, L, v), (bilinear form, function, linear form and scalar variable)."
+ )
if not is_true_ufl_scalar(v):
raise ValueError("Expecting scalar variable.")
from ufl.operators import diff
+
return diff(L, v) - action(diff(a, v), u)
diff --git a/ufl/functionspace.py b/ufl/functionspace.py
index 756c16aa0..834734274 100644
--- a/ufl/functionspace.py
+++ b/ufl/functionspace.py
@@ -9,9 +9,14 @@
# Modified by Massimiliano Leoni, 2016
# Modified by Cecile Daversin-Catty, 2018
+import typing
+
+import numpy as np
+
from ufl.core.ufl_type import UFLObject
from ufl.domain import join_domains
from ufl.duals import is_dual, is_primal
+from ufl.utils.sequences import product
# Export list for ufl.classes
__all_classes__ = [
@@ -46,7 +51,9 @@ def __init__(self, domain, element, label=""):
try:
domain_cell = domain.ufl_cell()
except AttributeError:
- raise ValueError("Expected non-abstract domain for initalization of function space.")
+ raise ValueError(
+ "Expected non-abstract domain for initalization of function space."
+ )
else:
if element.cell != domain_cell:
raise ValueError("Non-matching cell of finite element and domain.")
@@ -55,6 +62,32 @@ def __init__(self, domain, element, label=""):
self._ufl_domain = domain
self._ufl_element = element
+ @property
+ def components(self) -> typing.Dict[typing.Tuple[int, ...], int]:
+ """Get the numbering of the components of the element of this space.
+
+ 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)
+ """
+ from ufl.pullback import SymmetricPullback
+
+ if isinstance(self._ufl_element.pullback, SymmetricPullback):
+ return self._ufl_element.pullback._symmetry
+
+ if len(self._ufl_element.sub_elements) == 0:
+ return {(): 0}
+
+ components = {}
+ offset = 0
+ c_offset = 0
+ for s in self.ufl_sub_spaces():
+ for i, j in enumerate(np.ndindex(s.value_shape)):
+ components[(offset + i,)] = c_offset + s.components[j]
+ c_offset += max(s.components.values()) + 1
+ offset += s.value_size
+ return components
+
def label(self):
"""Return label of boundary domains to differentiate restricted and unrestricted."""
return self._label
@@ -113,6 +146,16 @@ def __repr__(self):
"""Representation."""
return f"BaseFunctionSpace({self._ufl_domain!r}, {self._ufl_element!r})"
+ @property
+ def value_shape(self) -> typing.Tuple[int, ...]:
+ """Return the shape of the value space on a physical domain."""
+ return self._ufl_element.pullback.physical_value_shape(self._ufl_element, self._ufl_domain)
+
+ @property
+ def value_size(self) -> int:
+ """Return the integer product of the value shape on a physical domain."""
+ return product(self.value_shape)
+
class FunctionSpace(BaseFunctionSpace, UFLObject):
"""Representation of a Function space."""
@@ -186,14 +229,15 @@ def ufl_sub_spaces(self):
def _ufl_hash_data_(self):
"""UFL hash data."""
- return ("TensorProductFunctionSpace",) \
- + tuple(V._ufl_hash_data_() for V in self.ufl_sub_spaces())
+ return ("TensorProductFunctionSpace",) + tuple(
+ V._ufl_hash_data_() for V in self.ufl_sub_spaces()
+ )
def _ufl_signature_data_(self, renumbering):
"""UFL signature data."""
- return ("TensorProductFunctionSpace",) \
- + tuple(V._ufl_signature_data_(renumbering)
- for V in self.ufl_sub_spaces())
+ return ("TensorProductFunctionSpace",) + tuple(
+ V._ufl_signature_data_(renumbering) for V in self.ufl_sub_spaces()
+ )
def __repr__(self):
"""Representation."""
@@ -219,10 +263,8 @@ def __init__(self, *args):
raise ValueError("Expecting BaseFunctionSpace objects")
# A mixed FS is only primal/dual if all the subspaces are primal/dual"
- self._primal = all([is_primal(subspace)
- for subspace in self._ufl_function_spaces])
- self._dual = all([is_dual(subspace)
- for subspace in self._ufl_function_spaces])
+ self._primal = all([is_primal(subspace) for subspace in self._ufl_function_spaces])
+ self._dual = all([is_dual(subspace) for subspace in self._ufl_function_spaces])
def ufl_sub_spaces(self):
"""Return ufl sub spaces."""
@@ -244,13 +286,13 @@ def dual(self, *args):
the original components in the other positions.
"""
if args:
- spaces = [space.dual() if i in args else space
- for i, space in enumerate(self._ufl_function_spaces)]
+ spaces = [
+ space.dual() if i in args else space
+ for i, space in enumerate(self._ufl_function_spaces)
+ ]
return MixedFunctionSpace(*spaces)
else:
- return MixedFunctionSpace(
- *[space.dual()for space in self._ufl_function_spaces]
- )
+ return MixedFunctionSpace(*[space.dual() for space in self._ufl_function_spaces])
def ufl_elements(self):
"""Return ufl elements."""
@@ -264,7 +306,8 @@ def ufl_element(self):
raise ValueError(
"Found multiple elements. Cannot return only one. "
"Consider building a FunctionSpace from a MixedElement "
- "in case of homogeneous dimension.")
+ "in case of homogeneous dimension."
+ )
def ufl_domains(self):
"""Return ufl domains."""
@@ -289,14 +332,13 @@ def num_sub_spaces(self):
def _ufl_hash_data_(self):
"""UFL hash data."""
- return ("MixedFunctionSpace",) \
- + tuple(V._ufl_hash_data_() for V in self.ufl_sub_spaces())
+ return ("MixedFunctionSpace",) + tuple(V._ufl_hash_data_() for V in self.ufl_sub_spaces())
def _ufl_signature_data_(self, renumbering):
"""UFL signature data."""
- return ("MixedFunctionSpace",) \
- + tuple(V._ufl_signature_data_(renumbering)
- for V in self.ufl_sub_spaces())
+ return ("MixedFunctionSpace",) + tuple(
+ V._ufl_signature_data_(renumbering) for V in self.ufl_sub_spaces()
+ )
def __repr__(self):
"""Representation."""
diff --git a/ufl/geometry.py b/ufl/geometry.py
index a8f6c7720..1acaf40a2 100644
--- a/ufl/geometry.py
+++ b/ufl/geometry.py
@@ -74,6 +74,7 @@
# --- Expression node types
+
@ufl_type(is_abstract=True)
class GeometricQuantity(Terminal):
"""Geometric quantity."""
@@ -137,6 +138,7 @@ class GeometricFacetQuantity(GeometricQuantity):
# --- Coordinate represented in different coordinate systems
+
@ufl_type()
class SpatialCoordinate(GeometricCellQuantity):
"""The coordinate in a domain.
@@ -244,6 +246,7 @@ def is_cellwise_constant(self):
# --- Origin of coordinate systems in larger coordinate systems
+
@ufl_type()
class CellOrigin(GeometricCellQuantity):
"""The spatial coordinate corresponding to origin of a reference cell."""
@@ -292,6 +295,7 @@ def ufl_shape(self):
# --- Jacobians of mappings between coordinate systems
+
@ufl_type()
class Jacobian(GeometricCellQuantity):
r"""The Jacobian of the mapping from reference cell to spatial coordinates.
@@ -456,9 +460,10 @@ def __init__(self, domain):
@property
def ufl_shape(self):
"""Get the UFL shape."""
- cell = extract_unique_domain(self).ufl_cell()
+ domain = extract_unique_domain(self)
+ cell = domain.ufl_cell()
nv = cell.num_vertices()
- g = cell.geometric_dimension()
+ g = domain.geometric_dimension()
return (nv, g)
def is_cellwise_constant(self):
@@ -484,9 +489,10 @@ def __init__(self, domain):
@property
def ufl_shape(self):
"""Get the UFL shape."""
- cell = extract_unique_domain(self).ufl_cell()
+ domain = extract_unique_domain(self)
+ cell = domain.ufl_cell()
ne = cell.num_edges()
- g = cell.geometric_dimension()
+ g = domain.geometric_dimension()
return (ne, g)
def is_cellwise_constant(self):
@@ -512,7 +518,8 @@ def __init__(self, domain):
@property
def ufl_shape(self):
"""Get the UFL shape."""
- cell = extract_unique_domain(self).ufl_cell()
+ domain = extract_unique_domain(self)
+ cell = domain.ufl_cell()
facet_types = cell.facet_types()
# Raise exception for cells with more than one facet type e.g. prisms
@@ -520,7 +527,7 @@ def ufl_shape(self):
raise Exception(f"Cell type {cell} not supported.")
nfe = facet_types[0].num_edges()
- g = cell.geometric_dimension()
+ g = domain.geometric_dimension()
return (nfe, g)
def is_cellwise_constant(self):
@@ -531,11 +538,13 @@ def is_cellwise_constant(self):
# --- Determinants (signed or pseudo) of geometry mapping Jacobians
+
@ufl_type()
class JacobianDeterminant(GeometricCellQuantity):
"""The determinant of the Jacobian.
- Represents the signed determinant of a square Jacobian or the pseudo-determinant of a non-square Jacobian.
+ Represents the signed determinant of a square Jacobian or the
+ pseudo-determinant of a non-square Jacobian.
"""
__slots__ = ()
@@ -577,11 +586,13 @@ def is_cellwise_constant(self):
# --- Inverses (signed or pseudo) of geometry mapping Jacobians
+
@ufl_type()
class JacobianInverse(GeometricCellQuantity):
"""The inverse of the Jacobian.
- Represents the inverse of a square Jacobian or the pseudo-inverse of a non-square Jacobian.
+ Represents the inverse of a square Jacobian or the pseudo-inverse of
+ a non-square Jacobian.
"""
__slots__ = ()
@@ -613,7 +624,9 @@ def __init__(self, domain):
GeometricFacetQuantity.__init__(self, domain)
t = self._domain.topological_dimension()
if t < 2:
- raise ValueError("FacetJacobianInverse is only defined for topological dimensions >= 2.")
+ raise ValueError(
+ "FacetJacobianInverse is only defined for topological dimensions >= 2."
+ )
@property
def ufl_shape(self):
@@ -641,7 +654,9 @@ def __init__(self, domain):
GeometricFacetQuantity.__init__(self, domain)
t = self._domain.topological_dimension()
if t < 2:
- raise ValueError("CellFacetJacobianInverse is only defined for topological dimensions >= 2.")
+ raise ValueError(
+ "CellFacetJacobianInverse is only defined for topological dimensions >= 2."
+ )
@property
def ufl_shape(self):
@@ -657,6 +672,7 @@ def is_cellwise_constant(self):
# --- Types representing normal or tangent vectors
+
@ufl_type()
class FacetNormal(GeometricFacetQuantity):
"""The outwards pointing normal vector of the current facet."""
@@ -715,7 +731,9 @@ def ufl_shape(self):
t = self._domain.topological_dimension()
return (t,)
-# --- Types representing measures of the cell and entities of the cell, typically used for stabilisation terms
+
+# --- Types representing measures of the cell and entities of the cell,
+# typically used for stabilisation terms
# TODO: Clean up this set of types? Document!
@@ -802,6 +820,7 @@ class MaxFacetEdgeLength(GeometricFacetQuantity):
# --- Types representing other stuff
+
@ufl_type()
class CellOrientation(GeometricCellQuantity):
"""The orientation (+1/-1) of the current cell.
diff --git a/ufl/index_combination_utils.py b/ufl/index_combination_utils.py
index 1f7682ae0..8bd5087a8 100644
--- a/ufl/index_combination_utils.py
+++ b/ufl/index_combination_utils.py
@@ -233,4 +233,9 @@ def merge_overlapping_indices(afi, afid, bfi, bfid):
if len(free_indices) + 2 * len(repeated_indices) != an + bn:
raise ValueError("Expecting only twice repeated indices.")
- return tuple(free_indices), tuple(index_dimensions), tuple(repeated_indices), tuple(repeated_index_dimensions)
+ return (
+ tuple(free_indices),
+ tuple(index_dimensions),
+ tuple(repeated_indices),
+ tuple(repeated_index_dimensions),
+ )
diff --git a/ufl/indexed.py b/ufl/indexed.py
index b22f3d7ef..9f6525863 100644
--- a/ufl/indexed.py
+++ b/ufl/indexed.py
@@ -63,10 +63,13 @@ def __init__(self, expression, multiindex):
if len(shape) != len(multiindex):
raise ValueError(
f"Invalid number of indices ({len(multiindex)}) for tensor "
- f"expression of rank {len(expression.ufl_shape)}:\n {ufl_err_str(expression)}")
- if any(int(di) >= int(si) or int(di) < 0
- for si, di in zip(shape, multiindex)
- if isinstance(di, FixedIndex)):
+ f"expression of rank {len(expression.ufl_shape)}:\n {ufl_err_str(expression)}"
+ )
+ if any(
+ int(di) >= int(si) or int(di) < 0
+ for si, di in zip(shape, multiindex)
+ if isinstance(di, FixedIndex)
+ ):
raise ValueError("Fixed index out of range!")
# Build tuples of free index ids and dimensions
@@ -99,8 +102,7 @@ def evaluate(self, x, mapping, component, index_values, derivatives=()):
def __str__(self):
"""Format as a string."""
- return "%s[%s]" % (parstr(self.ufl_operands[0], self),
- self.ufl_operands[1])
+ return "%s[%s]" % (parstr(self.ufl_operands[0], self), self.ufl_operands[1])
def __getitem__(self, key):
"""Get an item."""
@@ -108,5 +110,7 @@ def __getitem__(self, key):
# So that one doesn't have to special case indexing of
# expressions without shape.
return self
- raise ValueError(f"Attempting to index with {ufl_err_str(key)}, "
- f"but object is already indexed: {ufl_err_str(self)}")
+ raise ValueError(
+ f"Attempting to index with {ufl_err_str(key)}, "
+ f"but object is already indexed: {ufl_err_str(self)}"
+ )
diff --git a/ufl/indexsum.py b/ufl/indexsum.py
index b13829484..38b0e1cce 100644
--- a/ufl/indexsum.py
+++ b/ufl/indexsum.py
@@ -6,7 +6,6 @@
#
# SPDX-License-Identifier: LGPL-3.0-or-later
-
from ufl.constantvalue import Zero
from ufl.core.expr import Expr, ufl_err_str
from ufl.core.multiindex import MultiIndex
@@ -36,25 +35,25 @@ def __new__(cls, summand, index):
# Simplification to zero
if isinstance(summand, Zero):
sh = summand.ufl_shape
- j, = index
+ (j,) = index
fi = summand.ufl_free_indices
fid = summand.ufl_index_dimensions
pos = fi.index(j.count())
- fi = fi[:pos] + fi[pos + 1:]
- fid = fid[:pos] + fid[pos + 1:]
+ fi = fi[:pos] + fi[pos + 1 :]
+ fid = fid[:pos] + fid[pos + 1 :]
return Zero(sh, fi, fid)
return Operator.__new__(cls)
def __init__(self, summand, index):
"""Initialise."""
- j, = index
+ (j,) = index
fi = summand.ufl_free_indices
fid = summand.ufl_index_dimensions
pos = fi.index(j.count())
self._dimension = fid[pos]
- self.ufl_free_indices = fi[:pos] + fi[pos + 1:]
- self.ufl_index_dimensions = fid[:pos] + fid[pos + 1:]
+ self.ufl_free_indices = fi[:pos] + fi[pos + 1 :]
+ self.ufl_index_dimensions = fid[:pos] + fid[pos + 1 :]
Operator.__init__(self, (summand, index))
def index(self):
@@ -72,16 +71,14 @@ def ufl_shape(self):
def evaluate(self, x, mapping, component, index_values):
"""Evaluate."""
- i, = self.ufl_operands[1]
+ (i,) = self.ufl_operands[1]
tmp = 0
for k in range(self._dimension):
index_values.push(i, k)
- tmp += self.ufl_operands[0].evaluate(x, mapping, component,
- index_values)
+ tmp += self.ufl_operands[0].evaluate(x, mapping, component, index_values)
index_values.pop()
return tmp
def __str__(self):
"""Format as a string."""
- return "sum_{%s} %s " % (str(self.ufl_operands[1]),
- parstr(self.ufl_operands[0], self))
+ return "sum_{%s} %s " % (str(self.ufl_operands[1]), parstr(self.ufl_operands[0], self))
diff --git a/ufl/integral.py b/ufl/integral.py
index d680f5a77..510f4ebd5 100644
--- a/ufl/integral.py
+++ b/ufl/integral.py
@@ -12,7 +12,6 @@
import ufl
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
# Export list for ufl.classes
@@ -22,11 +21,16 @@
class Integral(object):
"""An integral over a single domain."""
- __slots__ = ("_integrand", "_integral_type", "_ufl_domain", "_subdomain_id", "_metadata", "_subdomain_data")
+ __slots__ = (
+ "_integrand",
+ "_integral_type",
+ "_ufl_domain",
+ "_subdomain_id",
+ "_metadata",
+ "_subdomain_data",
+ )
- def __init__(
- self, integrand, integral_type, domain, subdomain_id, metadata, subdomain_data
- ):
+ def __init__(self, integrand, integral_type, domain, subdomain_id, metadata, subdomain_data):
"""Initialise."""
if not isinstance(integrand, Expr):
raise ValueError("Expecting integrand to be an Expr instance.")
@@ -38,9 +42,13 @@ def __init__(
self._subdomain_data = subdomain_data
def reconstruct(
- self, integrand=None,
- integral_type=None, domain=None, subdomain_id=None,
- metadata=None, subdomain_data=None
+ self,
+ integrand=None,
+ integral_type=None,
+ domain=None,
+ subdomain_id=None,
+ metadata=None,
+ subdomain_data=None,
):
"""Construct a new Integral object with some properties replaced with new values.
@@ -100,8 +108,9 @@ def __mul__(self, scalar):
def __rmul__(self, scalar):
"""Multiply."""
if not is_scalar_constant_expression(scalar):
- raise ValueError("An integral can only be multiplied by a "
- "globally constant scalar expression.")
+ raise ValueError(
+ "An integral can only be multiplied by a globally constant scalar expression."
+ )
return self.reconstruct(scalar * self._integrand)
def __str__(self):
@@ -113,24 +122,33 @@ def __str__(self):
def __repr__(self):
"""Representation."""
- return (f"Integral({self._integrand!r}, {self._integral_type!r}, {self._ufl_domain!r}, "
- f"{self._subdomain_id!r}, {self._metadata!r}, {self._subdomain_data!r})")
+ return (
+ f"Integral({self._integrand!r}, {self._integral_type!r}, {self._ufl_domain!r}, "
+ f"{self._subdomain_id!r}, {self._metadata!r}, {self._subdomain_data!r})"
+ )
def __eq__(self, other):
"""Check equality."""
- return (isinstance(other, Integral) and self._integral_type == other._integral_type and # noqa: W504
- self._ufl_domain == other._ufl_domain and self._subdomain_id == other._subdomain_id and # noqa: W504
- self._integrand == other._integrand and self._metadata == other._metadata and # noqa: W504
- id_or_none(self._subdomain_data) == id_or_none(other._subdomain_data))
+ return (
+ isinstance(other, Integral)
+ and self._integral_type == other._integral_type
+ and self._ufl_domain == other._ufl_domain
+ and self._subdomain_id == other._subdomain_id
+ and self._integrand == other._integrand
+ and self._metadata == other._metadata
+ and id_or_none(self._subdomain_data) == id_or_none(other._subdomain_data)
+ )
def __hash__(self):
"""Hash."""
# Assuming few collisions by ignoring hash(self._metadata) (a
# dict is not hashable but we assume it is immutable in
# practice)
- hashdata = (hash(self._integrand),
- self._integral_type,
- hash(self._ufl_domain),
- self._subdomain_id,
- id_or_none(self._subdomain_data))
+ hashdata = (
+ hash(self._integrand),
+ self._integral_type,
+ hash(self._ufl_domain),
+ self._subdomain_id,
+ id_or_none(self._subdomain_data),
+ )
return hash(hashdata)
diff --git a/ufl/mathfunctions.py b/ufl/mathfunctions.py
index 704304d6c..4c2d87996 100644
--- a/ufl/mathfunctions.py
+++ b/ufl/mathfunctions.py
@@ -13,8 +13,16 @@
import numbers
import warnings
-from ufl.constantvalue import (ComplexValue, ConstantValue, FloatValue, IntValue, RealValue, Zero, as_ufl,
- is_true_ufl_scalar)
+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
@@ -42,6 +50,7 @@
# --- Function representations ---
+
@ufl_type(is_abstract=True, is_scalar=True, num_ops=1)
class MathFunction(Operator):
"""Base class for all unary scalar math functions."""
@@ -65,7 +74,9 @@ def evaluate(self, x, mapping, component, index_values):
else:
res = getattr(cmath, self._name)(a)
except ValueError:
- warnings.warn('Value error in evaluation of function %s with argument %s.' % (self._name, a))
+ warnings.warn(
+ "Value error in evaluation of function %s with argument %s." % (self._name, a)
+ )
raise
return res
@@ -344,9 +355,11 @@ def evaluate(self, x, mapping, component, index_values):
try:
res = math.atan2(a, b)
except TypeError:
- raise ValueError('Atan2 does not support complex numbers.')
+ raise ValueError("Atan2 does not support complex numbers.")
except ValueError:
- warnings.warn('Value error in evaluation of function atan2 with arguments %s, %s.' % (a, b))
+ warnings.warn(
+ "Value error in evaluation of function atan2 with arguments %s, %s." % (a, b)
+ )
raise
return res
@@ -383,7 +396,7 @@ def evaluate(self, x, mapping, component, index_values):
class BesselFunction(Operator):
"""Base class for all bessel functions."""
- __slots__ = ("_name")
+ __slots__ = "_name"
def __init__(self, name, nu, argument):
"""Initialise."""
@@ -410,22 +423,22 @@ def evaluate(self, x, mapping, component, index_values):
try:
import scipy.special
except ImportError:
- raise ValueError("You must have scipy installed to evaluate bessel functions in python.")
+ raise ValueError(
+ "You must have scipy installed to evaluate bessel functions in python."
+ )
name = self._name[-1]
if isinstance(self.ufl_operands[0], IntValue):
nu = int(self.ufl_operands[0])
- functype = 'n' if name != 'i' else 'v'
+ functype = "n" if name != "i" else "v"
else:
- nu = self.ufl_operands[0].evaluate(x, mapping, component,
- index_values)
- functype = 'v'
+ nu = self.ufl_operands[0].evaluate(x, mapping, component, index_values)
+ functype = "v"
func = getattr(scipy.special, name + functype)
return func(nu, a)
def __str__(self):
"""Format as a string."""
- return "%s(%s, %s)" % (self._name, self.ufl_operands[0],
- self.ufl_operands[1])
+ return "%s(%s, %s)" % (self._name, self.ufl_operands[0], self.ufl_operands[1])
@ufl_type()
diff --git a/ufl/matrix.py b/ufl/matrix.py
index 7fb1f5c07..039d70e23 100644
--- a/ufl/matrix.py
+++ b/ufl/matrix.py
@@ -30,12 +30,12 @@ class Matrix(BaseForm, Counted):
"_ufl_shape",
"_arguments",
"_coefficients",
- "_domains")
+ "_domains",
+ )
def __getnewargs__(self):
"""Get new args."""
- return (self._ufl_function_spaces[0], self._ufl_function_spaces[1],
- self._count)
+ return (self._ufl_function_spaces[0], self._ufl_function_spaces[1], self._count)
def __init__(self, row_space, column_space, count=None):
"""Initialise."""
@@ -53,7 +53,10 @@ def __init__(self, row_space, column_space, count=None):
self.ufl_operands = ()
self._domains = None
self._hash = None
- self._repr = f"Matrix({self._ufl_function_spaces[0]!r}, {self._ufl_function_spaces[1]!r}, {self._count!r})"
+ self._repr = (
+ f"Matrix({self._ufl_function_spaces[0]!r} "
+ f"{self._ufl_function_spaces[1]!r}, {self._count!r})"
+ )
def ufl_function_spaces(self):
"""Get the tuple of function spaces of this coefficient."""
@@ -61,8 +64,10 @@ def ufl_function_spaces(self):
def _analyze_form_arguments(self):
"""Define arguments of a matrix when considered as a form."""
- self._arguments = (Argument(self._ufl_function_spaces[0], 0),
- Argument(self._ufl_function_spaces[1], 1))
+ self._arguments = (
+ Argument(self._ufl_function_spaces[0], 0),
+ Argument(self._ufl_function_spaces[1], 1),
+ )
self._coefficients = ()
def _analyze_domains(self):
@@ -96,4 +101,6 @@ def equals(self, other):
return False
if self is other:
return True
- return self._count == other._count and self._ufl_function_spaces == other._ufl_function_spaces
+ return (
+ self._count == other._count and self._ufl_function_spaces == other._ufl_function_spaces
+ )
diff --git a/ufl/measure.py b/ufl/measure.py
index 97d719501..08f1fa803 100644
--- a/ufl/measure.py
+++ b/ufl/measure.py
@@ -29,20 +29,22 @@
_integral_types = [
# === Integration over full topological dimension:
("cell", "dx"), # Over cells of a mesh
-
# === Integration over topological dimension - 1:
("exterior_facet", "ds"), # Over one-sided exterior facets of a mesh
("interior_facet", "dS"), # Over two-sided facets between pairs of adjacent cells of a mesh
-
# === Integration over topological dimension 0
("vertex", "dP"), # Over vertices of a mesh
-
# === Integration over custom domains
("custom", "dc"), # Over custom user-defined domains (run-time quadrature points)
("cutcell", "dC"), # Over a cell with some part cut away (run-time quadrature points)
- ("interface", "dI"), # Over a facet fragment overlapping with two or more cells (run-time quadrature points)
- ("overlap", "dO"), # Over a cell fragment overlapping with two or more cells (run-time quadrature points)
-
+ (
+ "interface",
+ "dI",
+ ), # Over a facet fragment overlapping with two or more cells (run-time quadrature points)
+ (
+ "overlap",
+ "dO",
+ ), # Over a cell fragment overlapping with two or more cells (run-time quadrature points)
# === Firedrake specifics:
("exterior_facet_bottom", "ds_b"), # Over bottom facets on extruded mesh
("exterior_facet_top", "ds_t"), # Over top facets on extruded mesh
@@ -73,8 +75,7 @@ def register_integral_type(integral_type, measure_name):
def as_integral_type(integral_type):
"""Map short name to long name and require a valid one."""
integral_type = integral_type.replace(" ", "_")
- integral_type = measure_name_to_integral_type.get(integral_type,
- integral_type)
+ integral_type = measure_name_to_integral_type.get(integral_type, integral_type)
if integral_type not in integral_type_to_measure_name:
raise ValueError("Invalid integral_type.")
return integral_type
@@ -100,12 +101,14 @@ class Measure(object):
__slots__ = ("_integral_type", "_domain", "_subdomain_id", "_metadata", "_subdomain_data")
- def __init__(self,
- integral_type, # "dx" etc
- domain=None,
- subdomain_id="everywhere",
- metadata=None,
- subdomain_data=None):
+ def __init__(
+ self,
+ integral_type, # "dx" etc
+ domain=None,
+ subdomain_id="everywhere",
+ metadata=None,
+ subdomain_data=None,
+ ):
"""Initialise.
Args:
@@ -177,12 +180,9 @@ def metadata(self):
"""
return self._metadata
- def reconstruct(self,
- integral_type=None,
- subdomain_id=None,
- domain=None,
- metadata=None,
- subdomain_data=None):
+ def reconstruct(
+ self, integral_type=None, subdomain_id=None, domain=None, metadata=None, subdomain_data=None
+ ):
"""Construct a new Measure object with some properties replaced with new values.
Example:
@@ -202,9 +202,13 @@ def reconstruct(self,
metadata = self.metadata()
if subdomain_data is None:
subdomain_data = self.subdomain_data()
- return Measure(self.integral_type(),
- domain=domain, subdomain_id=subdomain_id,
- metadata=metadata, subdomain_data=subdomain_data)
+ return Measure(
+ self.integral_type(),
+ domain=domain,
+ subdomain_id=subdomain_id,
+ metadata=metadata,
+ subdomain_data=subdomain_data,
+ )
def subdomain_data(self):
"""Return the integral subdomain_data.
@@ -218,12 +222,18 @@ def subdomain_data(self):
# Note: Must keep the order of the first two arguments here
# (subdomain_id, metadata) for backwards compatibility, because
# some tutorials write e.g. dx(0, {...}) to set metadata.
- def __call__(self, subdomain_id=None, metadata=None, domain=None,
- subdomain_data=None, degree=None, scheme=None):
+ def __call__(
+ self,
+ subdomain_id=None,
+ metadata=None,
+ domain=None,
+ subdomain_data=None,
+ degree=None,
+ scheme=None,
+ ):
"""Reconfigure measure with new domain specification or metadata."""
# Let syntax dx() mean integral over everywhere
- all_args = (subdomain_id, metadata, domain, subdomain_data,
- degree, scheme)
+ all_args = (subdomain_id, metadata, domain, subdomain_data, degree, scheme)
if all(arg is None for arg in all_args):
return self.reconstruct(subdomain_id="everywhere")
@@ -234,7 +244,9 @@ def __call__(self, subdomain_id=None, metadata=None, domain=None,
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.")
+ raise ValueError(
+ "Ambiguous: setting domain both as keyword argument and first argument."
+ )
subdomain_id, domain = "everywhere", subdomain_id
# If degree or scheme is set, inject into metadata. This is a
@@ -250,9 +262,12 @@ def __call__(self, subdomain_id=None, metadata=None, domain=None,
# If we get any keywords, use them to reconstruct Measure.
# Note that if only one argument is given, it is the
# subdomain_id, e.g. dx(3) == dx(subdomain_id=3)
- return self.reconstruct(subdomain_id=subdomain_id, domain=domain,
- metadata=metadata,
- subdomain_data=subdomain_data)
+ return self.reconstruct(
+ subdomain_id=subdomain_id,
+ domain=domain,
+ metadata=metadata,
+ subdomain_data=subdomain_data,
+ )
def __str__(self):
"""Format as a string."""
@@ -268,7 +283,7 @@ def __str__(self):
if self._subdomain_data is not None:
args.append("subdomain_data=%s" % (self._subdomain_data,))
- return "%s(%s)" % (name, ', '.join(args))
+ return "%s(%s)" % (name, ", ".join(args))
def __repr__(self):
"""Return a repr string for this Measure."""
@@ -284,17 +299,19 @@ def __repr__(self):
if self._subdomain_data is not None:
args.append("subdomain_data=%s" % repr(self._subdomain_data))
- r = "%s(%s)" % (type(self).__name__, ', '.join(args))
+ r = "%s(%s)" % (type(self).__name__, ", ".join(args))
return r
def __hash__(self):
"""Return a hash value for this Measure."""
metadata_hashdata = tuple(sorted((k, id(v)) for k, v in list(self._metadata.items())))
- hashdata = (self._integral_type,
- self._subdomain_id,
- hash(self._domain),
- metadata_hashdata,
- id_or_none(self._subdomain_data))
+ hashdata = (
+ self._integral_type,
+ self._subdomain_id,
+ hash(self._domain),
+ metadata_hashdata,
+ id_or_none(self._subdomain_data),
+ )
return hash(hashdata)
def __eq__(self, other):
@@ -302,10 +319,14 @@ def __eq__(self, other):
sorted_metadata = sorted((k, id(v)) for k, v in list(self._metadata.items()))
sorted_other_metadata = sorted((k, id(v)) for k, v in list(other._metadata.items()))
- return (isinstance(other, Measure) and self._integral_type == other._integral_type and # noqa: W504
- self._subdomain_id == other._subdomain_id and self._domain == other._domain and # noqa: W504
- id_or_none(self._subdomain_data) == id_or_none(other._subdomain_data) and # noqa: W504
- sorted_metadata == sorted_other_metadata)
+ return (
+ isinstance(other, Measure)
+ and self._integral_type == other._integral_type
+ and self._subdomain_id == other._subdomain_id
+ and self._domain == other._domain
+ and id_or_none(self._subdomain_data) == id_or_none(other._subdomain_data)
+ and sorted_metadata == sorted_other_metadata
+ )
def __add__(self, other):
"""Add two measures (self+other).
@@ -361,18 +382,33 @@ def __rmul__(self, integrand):
raise ValueError(
"Can only integrate scalar expressions. The integrand is a "
f"tensor expression with value shape {integrand.ufl_shape} and "
- f"free indices with labels {integrand.ufl_free_indices}.")
+ f"free indices with labels {integrand.ufl_free_indices}."
+ )
# If we have a tuple of domain ids build the integrals one by
# one and construct as a Form in one go.
subdomain_id = self.subdomain_id()
if isinstance(subdomain_id, tuple):
- return Form(list(chain(*((integrand * self.reconstruct(subdomain_id=d)).integrals()
- for d in subdomain_id))))
+ return Form(
+ list(
+ chain(
+ *(
+ (integrand * self.reconstruct(subdomain_id=d)).integrals()
+ for d in subdomain_id
+ )
+ )
+ )
+ )
# Check that we have an integer subdomain or a string
# ("everywhere" or "otherwise", any more?)
- if not isinstance(subdomain_id, (str, numbers.Integral,)):
+ if not isinstance(
+ subdomain_id,
+ (
+ str,
+ numbers.Integral,
+ ),
+ ):
raise ValueError("Expecting integer or string domain id.")
# If we don't have an integration domain, try to find one in
@@ -381,19 +417,23 @@ def __rmul__(self, integrand):
if domain is None:
domains = extract_domains(integrand)
if len(domains) == 1:
- domain, = domains
+ (domain,) = domains
elif len(domains) == 0:
raise ValueError("This integral is missing an integration domain.")
else:
- raise ValueError("Multiple domains found, making the choice of integration domain ambiguous.")
+ raise ValueError(
+ "Multiple domains found, making the choice of integration domain ambiguous."
+ )
# Otherwise create and return a one-integral form
- integral = Integral(integrand=integrand,
- integral_type=self.integral_type(),
- domain=domain,
- subdomain_id=subdomain_id,
- metadata=self.metadata(),
- subdomain_data=self.subdomain_data())
+ integral = Integral(
+ integrand=integrand,
+ integral_type=self.integral_type(),
+ domain=domain,
+ subdomain_id=subdomain_id,
+ metadata=self.metadata(),
+ subdomain_data=self.subdomain_data(),
+ )
return Form([integral])
diff --git a/ufl/objects.py b/ufl/objects.py
index fc57ea2ae..64704d275 100644
--- a/ufl/objects.py
+++ b/ufl/objects.py
@@ -26,16 +26,16 @@
dX = dx + dC # noqa: F821
# Create objects for builtin known cell types
-vertex = Cell("vertex", 0)
-interval = Cell("interval", 1)
-triangle = Cell("triangle", 2)
-tetrahedron = Cell("tetrahedron", 3)
-prism = Cell("prism", 3)
-pyramid = Cell("pyramid", 3)
-quadrilateral = Cell("quadrilateral", 2)
-hexahedron = Cell("hexahedron", 3)
-tesseract = Cell("tesseract", 4)
-pentatope = Cell("pentatope", 4)
+vertex = Cell("vertex")
+interval = Cell("interval")
+triangle = Cell("triangle")
+tetrahedron = Cell("tetrahedron")
+prism = Cell("prism")
+pyramid = Cell("pyramid")
+quadrilateral = Cell("quadrilateral")
+hexahedron = Cell("hexahedron")
+tesseract = Cell("tesseract")
+pentatope = Cell("pentatope")
# Facet is just a dummy declaration for RestrictedElement
facet = "facet"
diff --git a/ufl/operators.py b/ufl/operators.py
index 78226d8a1..2ca1158f5 100644
--- a/ufl/operators.py
+++ b/ufl/operators.py
@@ -22,17 +22,57 @@
from ufl.averaging import CellAvg, FacetAvg
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.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.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.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
@@ -53,13 +93,15 @@ def shape(f):
# --- Complex operators ---
+
def conj(f):
"""The complex conjugate of f."""
f = as_ufl(f)
return Conj(f)
-# Alias because both conj and conjugate are in numpy and we wish to be consistent.
+# Alias because both conj and conjugate are in numpy and we wish to be
+# consistent.
conjugate = conj
@@ -77,6 +119,7 @@ def imag(f):
# --- Elementwise tensor operators ---
+
def elem_op_items(op_ind, indices, *args):
"""Elem op items."""
sh = args[0].ufl_shape
@@ -93,17 +136,22 @@ def extind(ii):
def elem_op(op, *args):
- """Take the elementwise application of operator op on scalar values from one or more tensor arguments."""
+ """Apply element-wise operations.
+
+ Take the element-wise application of operator op on scalar values
+ from one or more tensor arguments.
+ """
args = [as_ufl(arg) for arg in args]
sh = args[0].ufl_shape
if not all(sh == x.ufl_shape for x in args):
- raise ValueError("Cannot take elementwise operation with different shapes.")
+ raise ValueError("Cannot take element-wise operation with different shapes.")
if sh == ():
return op(*args)
def op_ind(ind, *args):
return op(*[x[ind] for x in args])
+
return as_tensor(elem_op_items(op_ind, (), *args))
@@ -124,6 +172,7 @@ def elem_pow(A, B):
# --- Tensor operators ---
+
def transpose(A):
"""Take the transposed of tensor A."""
A = as_ufl(A)
@@ -223,7 +272,10 @@ def tr(A):
def diag(A):
- """Take the diagonal part of rank 2 tensor A or make a diagonal rank 2 tensor from a rank 1 tensor.
+ """Diagonal ranl-2 tensor.
+
+ Take the diagonal part of rank 2 tensor A or make a diagonal rank 2
+ tensor from a rank 1 tensor.
Always returns a rank 2 tensor. See also diag_vector.
"""
@@ -232,7 +284,7 @@ def diag(A):
# Get and check dimensions
r = len(A.ufl_shape)
if r == 1:
- n, = A.ufl_shape
+ (n,) = A.ufl_shape
elif r == 2:
m, n = A.ufl_shape
if m != n:
@@ -287,6 +339,7 @@ def sym(A):
# --- Differential operators
+
def Dx(f, *i):
"""Take the partial derivative of f with respect to spatial variable number i.
@@ -315,6 +368,7 @@ def diff(f, v):
# Apply to integrands
if isinstance(f, Form):
from ufl.algorithms.map_integrands import map_integrands
+
return map_integrands(lambda e: diff(e, v), f)
# Apply to expression
@@ -400,21 +454,24 @@ def curl(f):
# --- DG operators ---
+
def jump(v, n=None):
"""Take the jump of v across a facet."""
v = as_ufl(v)
is_constant = len(extract_domains(v)) > 0
if is_constant:
if n is None:
- return v('+') - v('-')
+ return v("+") - v("-")
r = len(v.ufl_shape)
if r == 0:
- return v('+') * n('+') + v('-') * n('-')
+ return v("+") * n("+") + v("-") * n("-")
else:
- return dot(v('+'), n('+')) + dot(v('-'), n('-'))
+ return dot(v("+"), n("+")) + dot(v("-"), n("-"))
else:
- warnings.warn("Returning zero from jump of expression without a domain. "
- "This may be erroneous if a dolfin.Expression is involved.")
+ warnings.warn(
+ "Returning zero from jump of expression without a domain. "
+ "This may be erroneous if a dolfin.Expression is involved."
+ )
# FIXME: Is this right? If v has no domain, it doesn't depend
# on anything spatially variable or any form arguments, and
# thus the jump is zero. In other words, I'm assuming that "v
@@ -427,7 +484,7 @@ def jump(v, n=None):
def avg(v):
"""Take the average of v across a facet."""
v = as_ufl(v)
- return 0.5 * (v('+') + v('-'))
+ return 0.5 * (v("+") + v("-"))
def cell_avg(f):
@@ -442,6 +499,7 @@ def facet_avg(f):
# --- Other operators ---
+
def variable(e):
"""Define a variable representing the given expression.
@@ -453,6 +511,7 @@ def variable(e):
# --- Conditional expressions ---
+
def conditional(condition, true_value, false_value):
"""A conditional expression.
@@ -532,6 +591,7 @@ def min_value(x, y):
# --- Math functions ---
+
def _mathfunction(f, cls):
"""A mat function."""
f = as_ufl(f)
@@ -608,7 +668,7 @@ def atan2(f1, f2):
f1 = as_ufl(f1)
f2 = as_ufl(f2)
if isinstance(f1, (ComplexValue, complex)) or isinstance(f2, (ComplexValue, complex)):
- raise TypeError('atan2 is incompatible with complex numbers.')
+ raise TypeError("atan2 is incompatible with complex numbers.")
r = Atan2(f1, f2)
if isinstance(r, (RealValue, Zero, int, float)):
return float(r)
@@ -652,6 +712,7 @@ def bessel_K(nu, f):
# --- Special function for exterior_derivative
+
def exterior_derivative(f):
"""Take the exterior derivative of f.
@@ -693,7 +754,9 @@ def exterior_derivative(f):
except Exception:
raise ValueError(f"Unable to determine element from {f}")
- gdim = element.cell.geometric_dimension()
+ domain = f.ufl_domain()
+
+ gdim = domain.geometric_dimension()
space = element.sobolev_space
if space == sobolevspace.L2:
diff --git a/ufl/precedence.py b/ufl/precedence.py
index 0aea48b20..3b404dbda 100644
--- a/ufl/precedence.py
+++ b/ufl/precedence.py
@@ -16,7 +16,7 @@ def parstr(child, parent, pre="(", post=")", format=str):
# Execute when needed instead of on import, which leads to all
# kinds of circular trouble. Fixing this could be an optimization
# of str(expr) though.
- if not hasattr(parent, '_precedence'):
+ if not hasattr(parent, "_precedence"):
assign_precedences(build_precedence_list())
# We want child to be evaluated fully first, and if the parent has
@@ -41,8 +41,19 @@ def parstr(child, parent, pre="(", post=")", format=str):
def build_precedence_list():
"""Build precedence list."""
- from ufl.classes import (Abs, BesselFunction, Division, Indexed, IndexSum, MathFunction, Operator, Power, Product,
- Sum, Terminal)
+ from ufl.classes import (
+ Abs,
+ BesselFunction,
+ Division,
+ Indexed,
+ IndexSum,
+ MathFunction,
+ Operator,
+ Power,
+ Product,
+ Sum,
+ Terminal,
+ )
# TODO: Fill in other types...
# Power <= Transposed
@@ -57,7 +68,12 @@ def build_precedence_list():
# stronger than +, but weaker than product
precedence_list.append((IndexSum,))
- precedence_list.append((Product, Division,))
+ precedence_list.append(
+ (
+ Product,
+ Division,
+ )
+ )
# NB! Depends on language!
precedence_list.append((Power, MathFunction, BesselFunction, Abs))
@@ -75,6 +91,7 @@ def build_precedence_mapping(precedence_list):
Utility function used by some external code.
"""
from ufl.classes import Expr, abstract_classes, all_ufl_classes
+
pm = {}
missing = set()
# Assign integer values for each precedence level
@@ -103,4 +120,7 @@ def assign_precedences(precedence_list):
for c, p in sorted(pm.items(), key=lambda x: x[0].__name__):
c._precedence = p
if missing:
- warnings.warn("Missing precedence levels for classes:\n" + "\n".join(f" {c}" for c in sorted(missing)))
+ warnings.warn(
+ "Missing precedence levels for classes:\n"
+ + "\n".join(f" {c}" for c in sorted(missing))
+ )
diff --git a/ufl/protocols.py b/ufl/protocols.py
index df8041473..ad7dbb7f7 100644
--- a/ufl/protocols.py
+++ b/ufl/protocols.py
@@ -15,7 +15,7 @@ def id_or_none(obj):
"""
if obj is None:
return None
- elif hasattr(obj, 'ufl_id'):
+ elif hasattr(obj, "ufl_id"):
return obj.ufl_id()
else:
return id(obj)
diff --git a/ufl/pullback.py b/ufl/pullback.py
index dee12f0ce..0dc780e52 100644
--- a/ufl/pullback.py
+++ b/ufl/pullback.py
@@ -16,19 +16,33 @@
from ufl.core.expr import Expr
from ufl.core.multiindex import indices
from ufl.domain import extract_unique_domain
+from ufl.functionspace import FunctionSpace
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"]
+__all_classes__ = [
+ "NonStandardPullbackException",
+ "AbstractPullback",
+ "IdentityPullback",
+ "ContravariantPiola",
+ "CovariantPiola",
+ "L2Piola",
+ "DoubleContravariantPiola",
+ "DoubleCovariantPiola",
+ "CovariantContravariantPiola",
+ "MixedPullback",
+ "SymmetricPullback",
+ "PhysicalPullback",
+ "CustomPullback",
+ "UndefinedPullback",
+]
class NonStandardPullbackException(BaseException):
"""Exception to raise if a map is non-standard."""
+
pass
@@ -40,11 +54,12 @@ 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.
+ def physical_value_shape(self, element, domain) -> typing.Tuple[int, ...]:
+ """Get the physical value shape when this pull back is applied to an element on a domain.
Args:
element: The element that the pull back is applied to
+ domain: The domain
Returns:
The value shape when the pull back is applied to the given element
@@ -87,11 +102,12 @@ def apply(self, expr):
"""
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.
+ def physical_value_shape(self, element, domain) -> typing.Tuple[int, ...]:
+ """Get the physical value shape when this pull back is applied to an element on a domain.
Args:
element: The element that the pull back is applied to
+ domain: The domain
Returns:
The value shape when the pull back is applied to the given element
@@ -130,17 +146,18 @@ def apply(self, expr):
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.
+ def physical_value_shape(self, element, domain) -> typing.Tuple[int, ...]:
+ """Get the physical value shape when this pull back is applied to an element on a domain.
Args:
element: The element that the pull back is applied to
+ domain: The domain
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:]
+ gdim = domain.geometric_dimension()
+ return element.reference_value_shape[:-1] + (gdim,)
class CovariantPiola(AbstractPullback):
@@ -172,17 +189,18 @@ def apply(self, expr):
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.
+ def physical_value_shape(self, element, domain) -> typing.Tuple[int, ...]:
+ """Get the physical value shape when this pull back is applied to an element on a domain.
Args:
element: The element that the pull back is applied to
+ domain: The domain
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:]
+ gdim = domain.geometric_dimension()
+ return element.reference_value_shape[:-1] + (gdim,)
class L2Piola(AbstractPullback):
@@ -211,11 +229,12 @@ def apply(self, 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.
+ def physical_value_shape(self, element, domain) -> typing.Tuple[int, ...]:
+ """Get the physical value shape when this pull back is applied to an element on a domain.
Args:
element: The element that the pull back is applied to
+ domain: The domain
Returns:
The value shape when the pull back is applied to the given element
@@ -251,19 +270,20 @@ def apply(self, expr):
# 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))
+ 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.
+ def physical_value_shape(self, element, domain) -> typing.Tuple[int, ...]:
+ """Get the physical value shape when this pull back is applied to an element on a domain.
Args:
element: The element that the pull back is applied to
+ domain: The domain
Returns:
The value shape when the pull back is applied to the given element
"""
- gdim = element.cell.geometric_dimension()
- return (gdim, gdim)
+ gdim = domain.geometric_dimension()
+ return element.reference_value_shape[:-2] + (gdim, gdim)
class DoubleCovariantPiola(AbstractPullback):
@@ -295,17 +315,63 @@ def apply(self, expr):
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, ...]:
+ def physical_value_shape(self, element, domain) -> typing.Tuple[int, ...]:
+ """Get the physical value shape when this pull back is applied to an element on a domain.
+
+ Args:
+ element: The element that the pull back is applied to
+ domain: The domain
+
+ Returns:
+ The value shape when the pull back is applied to the given element
+ """
+ gdim = domain.geometric_dimension()
+ return element.reference_value_shape[:-2] + (gdim, gdim)
+
+
+class CovariantContravariantPiola(AbstractPullback):
+ """The covariant contravariant Piola pull back."""
+
+ def __repr__(self) -> str:
+ """Return a representation of the object."""
+ return "CovariantContravariantPiola()"
+
+ @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, JacobianInverse
+
+ domain = extract_unique_domain(expr)
+ J = Jacobian(domain)
+ detJ = JacobianDeterminant(J)
+ 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((1.0 / detJ) * K[m, i] * expr[kmn] * J[j, n], (*k, i, j))
+
+ def physical_value_shape(self, element, domain) -> 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
+ domain: The domain
Returns:
The value shape when the pull back is applied to the given element
"""
- gdim = element.cell.geometric_dimension()
- return (gdim, gdim)
+ gdim = domain.geometric_dimension()
+ return element.reference_value_shape[:-2] + (gdim, gdim)
class MixedPullback(AbstractPullback):
@@ -336,55 +402,65 @@ def apply(self, expr):
Returns: The function pulled back to the reference cell
"""
+ domain = extract_unique_domain(expr)
+ space = FunctionSpace(domain, self._element)
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))
+ 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}'")
+ f = as_tensor(np.asarray(g_components).reshape(space.value_shape))
+ if f.ufl_shape != space.value_shape:
+ raise ValueError(
+ "Expecting pulled back expression with shape "
+ f"'{space.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.
+ def physical_value_shape(self, element, domain) -> typing.Tuple[int, ...]:
+ """Get the physical value shape when this pull back is applied to an element on a domain.
Args:
element: The element that the pull back is applied to
+ domain: The domain
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, )
+ dim = sum(FunctionSpace(domain, 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]):
+ 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
+ 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
+ self._sub_element_value_shape = element.sub_elements[0].reference_value_shape
for e in element.sub_elements:
- if e.value_shape != self._sub_element_value_shape:
+ if e.reference_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()))
@@ -405,6 +481,8 @@ def apply(self, expr):
Returns: The function pulled back to the reference cell
"""
+ domain = extract_unique_domain(expr)
+ space = FunctionSpace(domain, self._element)
rflat = [expr[idx] for idx in np.ndindex(expr.ufl_shape)]
g_components = []
offsets = [0]
@@ -414,30 +492,37 @@ def apply(self, expr):
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))
+ 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}'")
+ f = as_tensor(np.asarray(g_components).reshape(space.value_shape))
+ if f.ufl_shape != space.value_shape:
+ raise ValueError(
+ f"Expecting pulled back expression with shape "
+ f"'{space.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.
+ def physical_value_shape(self, element, domain) -> typing.Tuple[int, ...]:
+ """Get the physical value shape when this pull back is applied to an element on a domain.
Args:
element: The element that the pull back is applied to
+ domain: The domain
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()))
+ assert isinstance(element, type(self._element))
+ subelem = element.sub_elements[0]
+ pvs = subelem.pullback.physical_value_shape(subelem, domain)
+ return tuple(i + 1 for i in max(self._symmetry.keys())) + pvs
class PhysicalPullback(AbstractPullback):
@@ -465,16 +550,17 @@ def apply(self, expr):
"""
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.
+ def physical_value_shape(self, element, domain) -> typing.Tuple[int, ...]:
+ """Get the physical value shape when this pull back is applied to an element on a domain.
Args:
element: The element that the pull back is applied to
+ domain: The domain
Returns:
The value shape when the pull back is applied to the given element
"""
- raise NotImplementedError()
+ return element.reference_value_shape
class CustomPullback(AbstractPullback):
@@ -502,16 +588,17 @@ def apply(self, expr):
"""
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.
+ def physical_value_shape(self, element, domain) -> typing.Tuple[int, ...]:
+ """Get the physical value shape when this pull back is applied to an element on a domain.
Args:
element: The element that the pull back is applied to
+ domain: The domain
Returns:
The value shape when the pull back is applied to the given element
"""
- raise NotImplementedError()
+ return element.reference_value_shape
class UndefinedPullback(AbstractPullback):
@@ -529,16 +616,17 @@ 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.
+ def physical_value_shape(self, element, domain) -> typing.Tuple[int, ...]:
+ """Get the physical value shape when this pull back is applied to an element on a domain.
Args:
element: The element that the pull back is applied to
+ domain: The domain
Returns:
The value shape when the pull back is applied to the given element
"""
- raise NotImplementedError()
+ return element.reference_value_shape
identity_pullback = IdentityPullback()
@@ -547,6 +635,7 @@ def physical_value_shape(self, element) -> typing.Tuple[int, ...]:
l2_piola = L2Piola()
double_covariant_piola = DoubleCovariantPiola()
double_contravariant_piola = DoubleContravariantPiola()
+covariant_contravariant_piola = CovariantContravariantPiola()
physical_pullback = PhysicalPullback()
custom_pullback = CustomPullback()
undefined_pullback = UndefinedPullback()
diff --git a/ufl/referencevalue.py b/ufl/referencevalue.py
index 5a4c5bb11..5dd82f4c3 100644
--- a/ufl/referencevalue.py
+++ b/ufl/referencevalue.py
@@ -10,10 +10,7 @@
from ufl.core.ufl_type import ufl_type
-@ufl_type(num_ops=1,
- is_index_free=True,
- is_terminal_modifier=True,
- is_in_reference_frame=True)
+@ufl_type(num_ops=1, is_index_free=True, is_terminal_modifier=True, is_in_reference_frame=True)
class ReferenceValue(Operator):
"""Representation of the reference cell value of a form argument."""
diff --git a/ufl/restriction.py b/ufl/restriction.py
index 2871cd53f..430fefa41 100644
--- a/ufl/restriction.py
+++ b/ufl/restriction.py
@@ -12,11 +12,13 @@
# --- Restriction operators ---
-@ufl_type(is_abstract=True,
- num_ops=1,
- inherit_shape_from_operand=0,
- inherit_indices_from_operand=0,
- is_restriction=True)
+@ufl_type(
+ is_abstract=True,
+ num_ops=1,
+ inherit_shape_from_operand=0,
+ inherit_indices_from_operand=0,
+ is_restriction=True,
+)
class Restricted(Operator):
"""Restriction."""
@@ -34,8 +36,7 @@ def side(self):
def evaluate(self, x, mapping, component, index_values):
"""Evaluate."""
- return self.ufl_operands[0].evaluate(x, mapping, component,
- index_values)
+ return self.ufl_operands[0].evaluate(x, mapping, component, index_values)
def __str__(self):
"""Format as a string."""
diff --git a/ufl/sobolevspace.py b/ufl/sobolevspace.py
index 9500ae422..6dfa83633 100644
--- a/ufl/sobolevspace.py
+++ b/ufl/sobolevspace.py
@@ -25,8 +25,9 @@
class SobolevSpace(object):
"""Symbolic representation of a Sobolev space.
- This implements a subset of the methods of a Python set so that finite elements and
- other Sobolev spaces can be tested for inclusion.
+ This implements a subset of the methods of a Python set so that
+ finite elements and other Sobolev spaces can be tested for
+ inclusion.
"""
def __init__(self, name, parents=None):
@@ -52,7 +53,8 @@ def __init__(self, name, parents=None):
"HCurl": 0,
"HEin": 0,
"HDivDiv": 0,
- "DirectionalH": 0
+ "HCurlDiv": 0,
+ "DirectionalH": 0,
}[self.name]
def __str__(self):
@@ -82,11 +84,11 @@ def __getitem__(self, spatial_index):
def __contains__(self, other):
"""Implement `fe in s` where `fe` is a FiniteElement and `s` is a SobolevSpace."""
if isinstance(other, SobolevSpace):
- raise TypeError("Unable to test for inclusion of a "
- "SobolevSpace in another SobolevSpace. "
- "Did you mean to use <= instead?")
- return (other.sobolev_space == self or
- self in other.sobolev_space.parents)
+ 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
def __lt__(self, other):
"""In common with intrinsic Python sets, < indicates "is a proper subset of"."""
@@ -98,7 +100,7 @@ class DirectionalSobolevSpace(SobolevSpace):
"""Directional Sobolev space.
Symbolic representation of a Sobolev space with varying smoothness
- in differerent spatial directions.
+ in different spatial directions.
"""
def __init__(self, orders):
@@ -110,8 +112,8 @@ def __init__(self, orders):
smoothness requirement is enforced.
"""
assert all(
- isinstance(x, int) or isinf(x)
- for x in orders), "Order must be an integer or infinity."
+ isinstance(x, int) or isinf(x) for x in orders
+ ), "Order must be an integer or infinity."
name = "DirectionalH"
parents = [L2]
super(DirectionalSobolevSpace, self).__init__(name, parents)
@@ -126,14 +128,19 @@ def __getitem__(self, spatial_index):
return spaces[self._orders[spatial_index]]
def __contains__(self, other):
- """Implement `fe in s` where `fe` is a FiniteElement and `s` is a DirectionalSobolevSpace."""
+ """Check if one space is contained in another.
+
+ Implement `fe in s` where `fe` is a FiniteElement and `s` is a
+ DirectionalSobolevSpace.
+ """
if isinstance(other, SobolevSpace):
- raise TypeError("Unable to test for inclusion of a "
- "SobolevSpace in another SobolevSpace. "
- "Did you mean to use <= instead?")
- return (other.sobolev_space == self or
- all(self[i] in other.sobolev_space.parents
- for i in self._spatial_indices))
+ raise TypeError(
+ "Unable to test for inclusion of a SobolevSpace in another SobolevSpace. "
+ "Did you mean to use <= instead?"
+ )
+ return other.sobolev_space == self or all(
+ self[i] in other.sobolev_space.parents for i in self._spatial_indices
+ )
def __eq__(self, other):
"""Check equality."""
@@ -146,17 +153,15 @@ def __lt__(self, other):
if isinstance(other, DirectionalSobolevSpace):
if self._spatial_indices != other._spatial_indices:
return False
- return any(self._orders[i] > other._orders[i]
- for i in self._spatial_indices)
+ return any(self._orders[i] > other._orders[i] for i in self._spatial_indices)
if other in [HDiv, HCurl]:
return all(self._orders[i] >= 1 for i in self._spatial_indices)
- elif other.name in ["HDivDiv", "HEin"]:
+ elif other.name in ["HDivDiv", "HEin", "HCurlDiv"]:
# Don't know how these spaces compare
return NotImplementedError(f"Don't know how to compare with {other.name}")
else:
- return any(
- self._orders[i] > other._order for i in self._spatial_indices)
+ return any(self._orders[i] > other._order for i in self._spatial_indices)
def __str__(self):
"""Format as a string."""
@@ -171,3 +176,4 @@ def __str__(self):
HInf = SobolevSpace("HInf", [H2, H1, HDiv, HCurl, L2])
HEin = SobolevSpace("HEin", [L2])
HDivDiv = SobolevSpace("HDivDiv", [L2])
+HCurlDiv = SobolevSpace("HCurlDiv", [L2])
diff --git a/ufl/sorting.py b/ufl/sorting.py
index 5efe2a44a..6b9324514 100644
--- a/ufl/sorting.py
+++ b/ufl/sorting.py
@@ -141,7 +141,7 @@ def cmp_expr(a, b):
bops = b.ufl_operands
# Sort by children in natural order
- for (r, s) in zip(aops, bops):
+ for r, s in zip(aops, bops):
# Skip subtree if objects are the same
if r is s:
continue
diff --git a/ufl/split_functions.py b/ufl/split_functions.py
index 886c98915..4f4cb4aaf 100644
--- a/ufl/split_functions.py
+++ b/ufl/split_functions.py
@@ -7,6 +7,8 @@
#
# Modified by Anders Logg, 2008
+from ufl.domain import extract_unique_domain
+from ufl.functionspace import FunctionSpace
from ufl.indexed import Indexed
from ufl.permutation import compute_indices
from ufl.tensors import ListTensor, as_matrix, as_vector
@@ -17,9 +19,11 @@
def split(v):
"""Split a coefficient or argument.
- If v is a Coefficient or Argument in a mixed space, returns
- a tuple with the function components corresponding to the subelements.
+ If v is a Coefficient or Argument in a mixed space, returns a tuple
+ with the function components corresponding to the subelements.
"""
+ domain = extract_unique_domain(v)
+
# Default range is all of v
begin = 0
end = None
@@ -38,8 +42,8 @@ def split(v):
# Get innermost terminal here and its element
v = args[0]
# Get relevant range of v components
- begin, = ops[0].ufl_operands[1]
- end, = ops[-1].ufl_operands[1]
+ (begin,) = ops[0].ufl_operands[1]
+ (end,) = ops[-1].ufl_operands[1]
begin = int(begin)
end = int(end) + 1
else:
@@ -54,10 +58,12 @@ def split(v):
return (v,)
if len(v.ufl_shape) != 1:
- raise ValueError("Don't know how to split tensor valued mixed functions without flattened index space.")
+ 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 = element.value_size
+ value_size = v.ufl_function_space().value_size
if end is None:
end = value_size
else:
@@ -66,12 +72,12 @@ def split(v):
j = begin
while True:
for e in element.sub_elements:
- if j < e.value_size:
+ if j < FunctionSpace(domain, e).value_size:
element = e
break
- j -= e.value_size
+ j -= FunctionSpace(domain, e).value_size
# Then break when we find the subelement that covers the whole range
- if element.value_size == (end - begin):
+ if FunctionSpace(domain, element).value_size == (end - begin):
break
# Build expressions representing the subfunction of v for each subelement
@@ -80,30 +86,33 @@ def split(v):
for i, e in enumerate(element.sub_elements):
# Get shape, size, indices, and v components
# corresponding to subelement value
- shape = e.value_shape
+ shape = FunctionSpace(domain, e).value_shape
strides = shape_to_strides(shape)
rank = len(shape)
sub_size = product(shape)
- subindices = [flatten_multiindex(c, strides)
- for c in compute_indices(shape)]
+ subindices = [flatten_multiindex(c, strides) for c in compute_indices(shape)]
components = [v[k + offset] for k in subindices]
# Shape components into same shape as subelement
if rank == 0:
- subv, = components
+ (subv,) = components
elif rank <= 1:
subv = as_vector(components)
elif rank == 2:
- subv = as_matrix([components[i * shape[1]: (i + 1) * shape[1]]
- for i in range(shape[0])])
+ subv = as_matrix(
+ [components[i * shape[1] : (i + 1) * shape[1]] for i in range(shape[0])]
+ )
else:
- raise ValueError(f"Don't know how to split functions with sub functions of rank {rank}.")
+ raise ValueError(
+ f"Don't know how to split functions with sub functions of rank {rank}."
+ )
offset += sub_size
sub_functions.append(subv)
if end != offset:
raise ValueError(
- "Function splitting failed to extract components for whole intended range. Something is wrong.")
+ "Function splitting failed to extract components for whole intended range."
+ )
return tuple(sub_functions)
diff --git a/ufl/tensoralgebra.py b/ufl/tensoralgebra.py
index 733b24388..4ec364191 100644
--- a/ufl/tensoralgebra.py
+++ b/ufl/tensoralgebra.py
@@ -41,6 +41,7 @@
# --- Classes representing compound tensor algebra operations ---
+
@ufl_type(is_abstract=True)
class CompoundTensorOperator(Operator):
"""Compount tensor operator."""
@@ -51,6 +52,7 @@ def __init__(self, operands):
"""Initialise."""
Operator.__init__(self, operands)
+
# TODO: Use this and make Sum handle scalars only?
# This would simplify some algorithms. The only
# problem is we can't use + in many algorithms because
@@ -144,8 +146,10 @@ def ufl_shape(self):
def __str__(self):
"""Format as a string."""
- return "%s (X) %s" % (parstr(self.ufl_operands[0], self),
- parstr(self.ufl_operands[1], self))
+ return "%s (X) %s" % (
+ parstr(self.ufl_operands[0], self),
+ parstr(self.ufl_operands[1], self),
+ )
@ufl_type(num_ops=2)
@@ -187,8 +191,7 @@ def __init__(self, a, b):
def __str__(self):
"""Format as a string."""
- return "%s : %s" % (parstr(self.ufl_operands[0], self),
- parstr(self.ufl_operands[1], self))
+ return "%s : %s" % (parstr(self.ufl_operands[0], self), parstr(self.ufl_operands[1], self))
@ufl_type(num_ops=2)
@@ -202,13 +205,14 @@ def __new__(cls, a, b):
ash = a.ufl_shape
bsh = b.ufl_shape
ar, br = len(ash), len(bsh)
- scalar = (ar == 0 and br == 0)
+ scalar = ar == 0 and br == 0
# Checks
if not ((ar >= 1 and br >= 1) or scalar):
raise ValueError(
"Dot product requires non-scalar arguments, "
- f"got arguments with ranks {ar} and {br}.")
+ f"got arguments with ranks {ar} and {br}."
+ )
if not (scalar or ash[-1] == bsh[0]):
raise ValueError("Dimension mismatch in dot product.")
@@ -236,8 +240,7 @@ def ufl_shape(self):
def __str__(self):
"""Format as a string."""
- return "%s . %s" % (parstr(self.ufl_operands[0], self),
- parstr(self.ufl_operands[1], self))
+ return "%s . %s" % (parstr(self.ufl_operands[0], self), parstr(self.ufl_operands[1], self))
@ufl_type(is_index_free=True, num_ops=1)
@@ -288,7 +291,8 @@ def __new__(cls, a, b):
if not (len(ash) == 1 and ash == bsh):
raise ValueError(
f"Cross product requires arguments of rank 1, got {ufl_err_str(a)} "
- f"and {ufl_err_str(b)}.")
+ f"and {ufl_err_str(b)}."
+ )
# Simplification
if isinstance(a, Zero) or isinstance(b, Zero):
@@ -308,8 +312,7 @@ def __init__(self, a, b):
def __str__(self):
"""Format as a string."""
- return "%s x %s" % (parstr(self.ufl_operands[0], self),
- parstr(self.ufl_operands[1], self))
+ return "%s x %s" % (parstr(self.ufl_operands[0], self), parstr(self.ufl_operands[1], self))
@ufl_type(num_ops=1, inherit_indices_from_operand=0)
@@ -468,7 +471,9 @@ def __new__(cls, A):
if len(sh) != 2:
raise ValueError("Deviatoric part of tensor with rank != 2 is undefined.")
if sh[0] != sh[1]:
- raise ValueError(f"Cannot take deviatoric part of rectangular matrix with dimensions {sh}.")
+ raise ValueError(
+ f"Cannot take deviatoric part of rectangular matrix with dimensions {sh}."
+ )
if A.ufl_free_indices:
raise ValueError("Not expecting free indices in Deviatoric.")
@@ -536,7 +541,9 @@ def __new__(cls, A):
if len(sh) != 2:
raise ValueError("Symmetric part of tensor with rank != 2 is undefined.")
if sh[0] != sh[1]:
- raise ValueError(f"Cannot take symmetric part of rectangular matrix with dimensions {sh}.")
+ raise ValueError(
+ f"Cannot take symmetric part of rectangular matrix with dimensions {sh}."
+ )
if Afi:
raise ValueError("Not expecting free indices in Sym.")
diff --git a/ufl/tensors.py b/ufl/tensors.py
index 2fe829990..3edb0759c 100644
--- a/ufl/tensors.py
+++ b/ufl/tensors.py
@@ -39,11 +39,17 @@ def __new__(cls, *expressions):
# Obviously, each subexpression must have the same shape
if any(sh != e.ufl_shape for e in expressions[1:]):
- raise ValueError("Cannot create a tensor by joining subexpressions with different shapes.")
+ raise ValueError(
+ "Cannot create a tensor by joining subexpressions with different shapes."
+ )
if any(fi != e.ufl_free_indices for e in expressions[1:]):
- raise ValueError("Cannot create a tensor where the components have different free indices.")
+ raise ValueError(
+ "Cannot create a tensor where the components have different free indices."
+ )
if any(fid != e.ufl_index_dimensions for e in expressions[1:]):
- raise ValueError("Cannot create a tensor where the components have different free index dimensions.")
+ raise ValueError(
+ "Cannot create a tensor where the components have different free index dimensions."
+ )
# Simplify to Zero if possible
if all(isinstance(e, Zero) for e in expressions):
@@ -59,7 +65,9 @@ def __init__(self, *expressions):
# Checks
indexset = set(self.ufl_operands[0].ufl_free_indices)
if not all(not (indexset ^ set(e.ufl_free_indices)) for e in self.ufl_operands):
- raise ValueError("Can't combine subtensor expressions with different sets of free indices.")
+ raise ValueError(
+ "Can't combine subtensor expressions with different sets of free indices."
+ )
@property
def ufl_shape(self):
@@ -71,7 +79,8 @@ def evaluate(self, x, mapping, component, index_values, derivatives=()):
if len(component) != len(self.ufl_shape):
raise ValueError(
"Can only evaluate scalars, expecting a component "
- "tuple of length {len(self.ufl_shape)}, not {component}.")
+ "tuple of length {len(self.ufl_shape)}, not {component}."
+ )
a = self.ufl_operands[component[0]]
component = component[1:]
if derivatives:
@@ -96,6 +105,7 @@ def __getitem__(self, key):
def __str__(self):
"""Format as a string."""
+
def substring(expressions, indent):
ind = " " * indent
if any(isinstance(e, ListTensor) for e in expressions):
@@ -110,6 +120,7 @@ def substring(expressions, indent):
else:
s = ", ".join(str(e) for e in expressions)
return "%s[%s]" % (ind, s)
+
return substring(self.ufl_operands, 0)
@@ -123,9 +134,11 @@ def __new__(cls, expression, indices):
"""Create a new ComponentTensor."""
# Simplify
if isinstance(expression, Zero):
- fi, fid, sh = remove_indices(expression.ufl_free_indices,
- expression.ufl_index_dimensions,
- [ind.count() for ind in indices])
+ fi, fid, sh = remove_indices(
+ expression.ufl_free_indices,
+ expression.ufl_index_dimensions,
+ [ind.count() for ind in indices],
+ )
return Zero(sh, fi, fid)
# Construct
@@ -144,9 +157,11 @@ def __init__(self, expression, indices):
Operator.__init__(self, (expression, indices))
- fi, fid, sh = remove_indices(expression.ufl_free_indices,
- expression.ufl_index_dimensions,
- [ind.count() for ind in indices])
+ fi, fid, sh = remove_indices(
+ expression.ufl_free_indices,
+ expression.ufl_index_dimensions,
+ [ind.count() for ind in indices],
+ )
self.ufl_free_indices = fi
self.ufl_index_dimensions = fid
self.ufl_shape = sh
@@ -190,9 +205,11 @@ def __str__(self):
# --- User-level functions to wrap expressions in the correct way ---
+
def numpy2nestedlists(arr):
"""Convert Numpy array to a nested list."""
from numpy import ndarray
+
if not isinstance(arr, ndarray):
return arr
return [numpy2nestedlists(arr[k]) for k in range(arr.shape[0])]
@@ -210,8 +227,9 @@ def _as_list_tensor(expressions):
def from_numpy_to_lists(expressions):
"""Convert Numpy array to lists."""
try:
- import numpy
- if isinstance(expressions, numpy.ndarray):
+ import numpy as np
+
+ if isinstance(expressions, np.ndarray):
if expressions.shape == ():
# Unwrap scalar ndarray
return expressions.item()
@@ -389,6 +407,7 @@ def unit_indexed_tensor(shape, component):
"""Unit indexed tensor."""
from ufl.constantvalue import Identity
from ufl.operators import outer # a bit of circular dependency issue here
+
r = len(shape)
if r == 0:
return 0, ()
diff --git a/ufl/utils/formatting.py b/ufl/utils/formatting.py
index 4df63cd98..7c2d4336a 100644
--- a/ufl/utils/formatting.py
+++ b/ufl/utils/formatting.py
@@ -16,7 +16,7 @@ def camel2underscore(name):
# Don't insert _ between multiple upper case letters
if lastlower:
letters.append("_")
- i = i.lower() # noqa: E741
+ i = i.lower()
lastlower = thislower
letters.append(i)
return "".join(letters)
@@ -45,7 +45,7 @@ def tstr(t, colsize=80):
# Pretty-print table
s = ""
- for (key, value) in t:
+ for key, value in t:
key = str(key)
if isinstance(value, str):
value = "'%s'" % value
@@ -85,7 +85,10 @@ def _tree_format_expression(expression, indentation, parentheses):
if expression._ufl_is_terminal_:
s = "%s%s" % (ind, repr(expression))
else:
- sops = [_tree_format_expression(o, indentation + 1, parentheses) for o in expression.ufl_operands]
+ sops = [
+ _tree_format_expression(o, indentation + 1, parentheses)
+ for o in expression.ufl_operands
+ ]
s = "%s%s\n" % (ind, expression._ufl_class_.__name__)
if parentheses and len(sops) > 1:
s += "%s(\n" % (ind,)
diff --git a/ufl/utils/indexflattening.py b/ufl/utils/indexflattening.py
index 0e5aea412..f9935e51a 100644
--- a/ufl/utils/indexflattening.py
+++ b/ufl/utils/indexflattening.py
@@ -1,4 +1,4 @@
-"""This module contains a collection of utilities for mapping between multiindices and a flattened index space."""
+"""Collection of utilities for mapping between multiindices and a flattened index space."""
# Copyright (C) 2008-2016 Martin Sandve Alnæs
#
diff --git a/ufl/utils/sequences.py b/ufl/utils/sequences.py
index 9904287c4..71c845349 100644
--- a/ufl/utils/sequences.py
+++ b/ufl/utils/sequences.py
@@ -8,7 +8,7 @@
from functools import reduce
-import numpy
+import numpy as np
def product(sequence):
@@ -21,11 +21,11 @@ def product(sequence):
def max_degree(degrees):
"""Maximum degree for mixture of scalar and tuple degrees."""
- # numpy.maximum broadcasts scalar degrees to tuple degrees if
- # necessary. reduce applies numpy.maximum pairwise.
- degree = reduce(numpy.maximum, map(numpy.asarray, degrees))
+ # np.maximum broadcasts scalar degrees to tuple degrees if
+ # necessary. reduce applies np.maximum pairwise.
+ degree = reduce(np.maximum, map(np.asarray, degrees))
if degree.ndim:
degree = tuple(map(int, degree)) # tuple degree
else:
- degree = int(degree) # scalar degree
+ degree = int(degree) # scalar degree
return degree
diff --git a/ufl/utils/sorting.py b/ufl/utils/sorting.py
index bf02cfdf6..896ca2099 100644
--- a/ufl/utils/sorting.py
+++ b/ufl/utils/sorting.py
@@ -48,10 +48,12 @@ def sorted_by_count(seq):
def sorted_by_key(mapping):
"""Sort dict items by key, allowing different key types."""
+
# Python3 doesn't allow comparing builtins of different type,
# therefore the typename trick here
def _key(x):
return (type(x[0]).__name__, x[0])
+
return sorted(mapping.items(), key=_key)
@@ -80,9 +82,13 @@ def canonicalize_metadata(metadata):
value = canonicalize_metadata(value)
elif isinstance(value, (int, float, str)) or value is None:
value = str(value)
+ elif hasattr(value, "ufl_signature"):
+ value = value.ufl_signature
else:
- warnings.warn(f"Applying str() to a metadata value of type {type(value).__name__}, "
- "don't know if this is safe.")
+ warnings.warn(
+ f"Applying str() to a metadata value of type {type(value).__name__}, "
+ "don't know if this is safe."
+ )
value = str(value)
newvalues.append(value)
diff --git a/ufl/utils/stacks.py b/ufl/utils/stacks.py
index a19a713de..da4c39614 100644
--- a/ufl/utils/stacks.py
+++ b/ufl/utils/stacks.py
@@ -24,7 +24,11 @@ def peek(self):
class StackDict(dict):
- """A dict that can be changed incrementally with 'd.push(k,v)' and have changes rolled back with 'k,v = d.pop()'."""
+ """A dictionary type.
+
+ A dict that can be changed incrementally with 'd.push(k,v)' and have
+ changes rolled back with 'k,v = d.pop()'.
+ """
def __init__(self, *args, **kwargs):
"""Initialise."""
diff --git a/ufl/variable.py b/ufl/variable.py
index 84a795434..ad671e10c 100644
--- a/ufl/variable.py
+++ b/ufl/variable.py
@@ -118,10 +118,12 @@ def label(self):
def __eq__(self, other):
"""Check equality."""
- return (isinstance(other, Variable) and self.ufl_operands[1] == other.ufl_operands[1] and # noqa: W504
- self.ufl_operands[0] == other.ufl_operands[0])
+ return (
+ isinstance(other, Variable)
+ and self.ufl_operands[1] == other.ufl_operands[1]
+ and self.ufl_operands[0] == other.ufl_operands[0]
+ )
def __str__(self):
"""Format as a string."""
- return "var%d(%s)" % (self.ufl_operands[1].count(),
- self.ufl_operands[0])
+ return "var%d(%s)" % (self.ufl_operands[1].count(), self.ufl_operands[0])