dolfinx-adjoint 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. dolfinx_adjoint-0.2.0/LICENSE +7 -0
  2. dolfinx_adjoint-0.2.0/PKG-INFO +151 -0
  3. dolfinx_adjoint-0.2.0/README.md +120 -0
  4. dolfinx_adjoint-0.2.0/pyproject.toml +111 -0
  5. dolfinx_adjoint-0.2.0/setup.cfg +4 -0
  6. dolfinx_adjoint-0.2.0/src/dolfinx_adjoint/__init__.py +37 -0
  7. dolfinx_adjoint-0.2.0/src/dolfinx_adjoint/assembly.py +93 -0
  8. dolfinx_adjoint-0.2.0/src/dolfinx_adjoint/blocks/__init__.py +7 -0
  9. dolfinx_adjoint-0.2.0/src/dolfinx_adjoint/blocks/_vector.py +77 -0
  10. dolfinx_adjoint-0.2.0/src/dolfinx_adjoint/blocks/assembly.py +322 -0
  11. dolfinx_adjoint-0.2.0/src/dolfinx_adjoint/blocks/function_assigner.py +198 -0
  12. dolfinx_adjoint-0.2.0/src/dolfinx_adjoint/blocks/solvers.py +1152 -0
  13. dolfinx_adjoint-0.2.0/src/dolfinx_adjoint/petsc_utils.py +89 -0
  14. dolfinx_adjoint-0.2.0/src/dolfinx_adjoint/solvers.py +248 -0
  15. dolfinx_adjoint-0.2.0/src/dolfinx_adjoint/types/__init__.py +3 -0
  16. dolfinx_adjoint-0.2.0/src/dolfinx_adjoint/types/function.py +291 -0
  17. dolfinx_adjoint-0.2.0/src/dolfinx_adjoint/utils.py +46 -0
  18. dolfinx_adjoint-0.2.0/src/dolfinx_adjoint.egg-info/PKG-INFO +151 -0
  19. dolfinx_adjoint-0.2.0/src/dolfinx_adjoint.egg-info/SOURCES.txt +25 -0
  20. dolfinx_adjoint-0.2.0/src/dolfinx_adjoint.egg-info/dependency_links.txt +1 -0
  21. dolfinx_adjoint-0.2.0/src/dolfinx_adjoint.egg-info/requires.txt +27 -0
  22. dolfinx_adjoint-0.2.0/src/dolfinx_adjoint.egg-info/top_level.txt +1 -0
  23. dolfinx_adjoint-0.2.0/tests/test_assemble_scalar.py +91 -0
  24. dolfinx_adjoint-0.2.0/tests/test_assign.py +161 -0
  25. dolfinx_adjoint-0.2.0/tests/test_linear_solver.py +90 -0
  26. dolfinx_adjoint-0.2.0/tests/test_poisson_mother.py +273 -0
  27. dolfinx_adjoint-0.2.0/tests/test_version.py +5 -0
@@ -0,0 +1,7 @@
1
+ Copyright 2023 Jørgen S. Dokken
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,151 @@
1
+ Metadata-Version: 2.4
2
+ Name: dolfinx-adjoint
3
+ Version: 0.2.0
4
+ Summary: Automatic differentation compatible with DOLFINx
5
+ Author-email: "Jørgen S. Dokken" <dokken@simula.no>
6
+ License-Expression: MIT
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Requires-Dist: fenics-dolfinx>=0.10.0
10
+ Requires-Dist: pyadjoint-ad>=2025.10.0
11
+ Requires-Dist: typing_extensions; python_version < "3.11"
12
+ Provides-Extra: test
13
+ Requires-Dist: pytest; extra == "test"
14
+ Provides-Extra: dev
15
+ Requires-Dist: pdbpp; extra == "dev"
16
+ Requires-Dist: ipython; extra == "dev"
17
+ Requires-Dist: mypy; extra == "dev"
18
+ Requires-Dist: ruff; extra == "dev"
19
+ Provides-Extra: docs
20
+ Requires-Dist: jupyter-book<2.0; extra == "docs"
21
+ Requires-Dist: jupytext; extra == "docs"
22
+ Requires-Dist: pandas; extra == "docs"
23
+ Requires-Dist: pyvista[all]>0.45; extra == "docs"
24
+ Requires-Dist: networkx; extra == "docs"
25
+ Requires-Dist: pygraphviz; extra == "docs"
26
+ Provides-Extra: all
27
+ Requires-Dist: dolfinx_adjoint[test]; extra == "all"
28
+ Requires-Dist: dolfinx_adjoint[dev]; extra == "all"
29
+ Requires-Dist: dolfinx_adjoint[docs]; extra == "all"
30
+ Dynamic: license-file
31
+
32
+ # DOLFINx-Adjoint
33
+
34
+ **DOLFINx-Adjoint** is an algorithmic differentiation (AD) framework for [DOLFINx](https://github.com/FEniCS/dolfinx). It allows you to automatically compute the gradients and Hessians of PDE-constrained optimization problems and track your computational graphs through the `pyadjoint` backend.
35
+
36
+ Read the [Latest Documentation here](https://scientificcomputing.github.io/dolfinx-adjoint).
37
+
38
+ > **Note for Legacy FEnICS Users:** If you are using the legacy FEnICS (`dolfin`) library, please refer to the original [dolfin-adjoint repository](https://github.com/dolfin-adjoint/dolfin-adjoint). This repository (DOLFINx-Adjoint) is built specifically for the modern DOLFINx environment and is currently under active development. It is intended to eventually support the same comprehensive feature set and capabilities as the legacy version.
39
+
40
+ ## Features
41
+
42
+ * **Automated Adjoints:** Seamlessly derive discrete adjoint models from DOLFINx forward models.
43
+
44
+ * **Overloaded API:** Swap out standard `dolfinx` calls with their `dolfinx_adjoint` equivalents (e.g., `Function`, `Constant`, `LinearProblem`, `NonlinearProblem`, `assemble_scalar`) to automatically record the computational tape.
45
+
46
+ * **Optimization:** Seamless integration with PDE-constrained optimization frameworks like [Moola](https://github.com/funsim/moola) or SciPy via `pyadjoint.ReducedFunctional`.
47
+
48
+ ## Installation
49
+
50
+ ### Dependencies
51
+
52
+ DOLFINx-Adjoint requires **DOLFINx** (>=0.10.0) and **pyadjoint-ad**.
53
+
54
+ ### via pip
55
+
56
+ The main way to install the package is via pip:
57
+
58
+ ```bash
59
+ python3 -m pip install dolfinx-adjoint
60
+ ```
61
+
62
+ ### Development Install
63
+
64
+ To install the latest development version directly from the repository, use:
65
+
66
+ ```bash
67
+ python3 -m pip install git+[https://github.com/scientificcomputing/dolfinx-adjoint.git](https://github.com/scientificcomputing/dolfinx-adjoint.git)
68
+ ```
69
+
70
+ If you plan to actively modify the code, clone the repository and install the optional dependencies for testing, development, and documentation generation:
71
+
72
+ ```bash
73
+ git clone [https://github.com/scientificcomputing/dolfinx-adjoint.git](https://github.com/scientificcomputing/dolfinx-adjoint.git)
74
+ cd dolfinx-adjoint
75
+ python3 -m pip install -e ".[all]"
76
+ ```
77
+
78
+ ### Docker
79
+
80
+ A pre-built Docker image is automatically published by the CI. You can pull the nightly build which comes with DOLFINx and DOLFINx-Adjoint pre-installed:
81
+
82
+ ```bash
83
+ docker run -ti ghcr.io/scientificcomputing/dolfinx-adjoint:v0.2.0
84
+ ```
85
+
86
+ *(Note: Adjust the tag to the latest release or build).*
87
+
88
+ ## Quick Start
89
+
90
+ Using `dolfinx_adjoint` is designed to be as close to standard `dolfinx` syntax as possible. Here is a brief overview of how to track a parameter and assemble an objective functional:
91
+ <!-- #endregion -->
92
+
93
+ ```python
94
+ import dolfinx
95
+ from mpi4py import MPI
96
+ import pyadjoint
97
+ import dolfinx_adjoint
98
+
99
+ # Create mesh and function space
100
+ mesh = dolfinx.mesh.create_unit_square(MPI.COMM_WORLD, 10, 10)
101
+ V = dolfinx.fem.functionspace(mesh, ("Lagrange", 1))
102
+
103
+ # Use dolfinx_adjoint overloaded types
104
+ # This ensures operations are tracked on the pyadjoint tape!
105
+ f = dolfinx_adjoint.Function(V, name="Control")
106
+ uh = dolfinx_adjoint.Function(V, name="State")
107
+
108
+ # ... Define your UFL forms ...
109
+
110
+ # Use overloaded solvers
111
+ problem = dolfinx_adjoint.LinearProblem(a, L, u=uh, bcs=[bc])
112
+ problem.solve()
113
+
114
+ # Assemble the objective scalar using the overloaded assembly
115
+ J_symbolic = 0.5 * ufl.inner(uh - d, uh - d) * ufl.dx
116
+ J = dolfinx_adjoint.assemble_scalar(J_symbolic)
117
+
118
+ # Create a ReducedFunctional for optimization
119
+ control = pyadjoint.Control(f)
120
+ Jhat = pyadjoint.ReducedFunctional(J, control)
121
+
122
+ # Evaluate gradient
123
+ gradient = Jhat.derivative()
124
+ ```
125
+
126
+ <!-- #region -->
127
+ For more comprehensive examples, such as solving the optimal control of the Poisson equation or time-distributed control problems, check out the `demos/` directory or the [online documentation](https://scientificcomputing.github.io/dolfinx-adjoint).
128
+
129
+ ## Development and Testing
130
+
131
+ Code formatting is enforced via `ruff` and type-checking via `mypy`. To set up your local development environment:
132
+
133
+ ```bash
134
+ # Install development dependencies
135
+ python3 -m pip install -e ".[dev,test]"
136
+
137
+ # Run formatting checks
138
+ ruff check .
139
+ ruff format --check .
140
+
141
+ # Run type checking
142
+ python3 -m mypy .
143
+
144
+ # Run tests
145
+ python3 -m pytest -vs tests/
146
+ ```
147
+
148
+ ## License
149
+
150
+ MIT License. See [LICENSE](LICENSE) for more details.
151
+ <!-- #endregion -->
@@ -0,0 +1,120 @@
1
+ # DOLFINx-Adjoint
2
+
3
+ **DOLFINx-Adjoint** is an algorithmic differentiation (AD) framework for [DOLFINx](https://github.com/FEniCS/dolfinx). It allows you to automatically compute the gradients and Hessians of PDE-constrained optimization problems and track your computational graphs through the `pyadjoint` backend.
4
+
5
+ Read the [Latest Documentation here](https://scientificcomputing.github.io/dolfinx-adjoint).
6
+
7
+ > **Note for Legacy FEnICS Users:** If you are using the legacy FEnICS (`dolfin`) library, please refer to the original [dolfin-adjoint repository](https://github.com/dolfin-adjoint/dolfin-adjoint). This repository (DOLFINx-Adjoint) is built specifically for the modern DOLFINx environment and is currently under active development. It is intended to eventually support the same comprehensive feature set and capabilities as the legacy version.
8
+
9
+ ## Features
10
+
11
+ * **Automated Adjoints:** Seamlessly derive discrete adjoint models from DOLFINx forward models.
12
+
13
+ * **Overloaded API:** Swap out standard `dolfinx` calls with their `dolfinx_adjoint` equivalents (e.g., `Function`, `Constant`, `LinearProblem`, `NonlinearProblem`, `assemble_scalar`) to automatically record the computational tape.
14
+
15
+ * **Optimization:** Seamless integration with PDE-constrained optimization frameworks like [Moola](https://github.com/funsim/moola) or SciPy via `pyadjoint.ReducedFunctional`.
16
+
17
+ ## Installation
18
+
19
+ ### Dependencies
20
+
21
+ DOLFINx-Adjoint requires **DOLFINx** (>=0.10.0) and **pyadjoint-ad**.
22
+
23
+ ### via pip
24
+
25
+ The main way to install the package is via pip:
26
+
27
+ ```bash
28
+ python3 -m pip install dolfinx-adjoint
29
+ ```
30
+
31
+ ### Development Install
32
+
33
+ To install the latest development version directly from the repository, use:
34
+
35
+ ```bash
36
+ python3 -m pip install git+[https://github.com/scientificcomputing/dolfinx-adjoint.git](https://github.com/scientificcomputing/dolfinx-adjoint.git)
37
+ ```
38
+
39
+ If you plan to actively modify the code, clone the repository and install the optional dependencies for testing, development, and documentation generation:
40
+
41
+ ```bash
42
+ git clone [https://github.com/scientificcomputing/dolfinx-adjoint.git](https://github.com/scientificcomputing/dolfinx-adjoint.git)
43
+ cd dolfinx-adjoint
44
+ python3 -m pip install -e ".[all]"
45
+ ```
46
+
47
+ ### Docker
48
+
49
+ A pre-built Docker image is automatically published by the CI. You can pull the nightly build which comes with DOLFINx and DOLFINx-Adjoint pre-installed:
50
+
51
+ ```bash
52
+ docker run -ti ghcr.io/scientificcomputing/dolfinx-adjoint:v0.2.0
53
+ ```
54
+
55
+ *(Note: Adjust the tag to the latest release or build).*
56
+
57
+ ## Quick Start
58
+
59
+ Using `dolfinx_adjoint` is designed to be as close to standard `dolfinx` syntax as possible. Here is a brief overview of how to track a parameter and assemble an objective functional:
60
+ <!-- #endregion -->
61
+
62
+ ```python
63
+ import dolfinx
64
+ from mpi4py import MPI
65
+ import pyadjoint
66
+ import dolfinx_adjoint
67
+
68
+ # Create mesh and function space
69
+ mesh = dolfinx.mesh.create_unit_square(MPI.COMM_WORLD, 10, 10)
70
+ V = dolfinx.fem.functionspace(mesh, ("Lagrange", 1))
71
+
72
+ # Use dolfinx_adjoint overloaded types
73
+ # This ensures operations are tracked on the pyadjoint tape!
74
+ f = dolfinx_adjoint.Function(V, name="Control")
75
+ uh = dolfinx_adjoint.Function(V, name="State")
76
+
77
+ # ... Define your UFL forms ...
78
+
79
+ # Use overloaded solvers
80
+ problem = dolfinx_adjoint.LinearProblem(a, L, u=uh, bcs=[bc])
81
+ problem.solve()
82
+
83
+ # Assemble the objective scalar using the overloaded assembly
84
+ J_symbolic = 0.5 * ufl.inner(uh - d, uh - d) * ufl.dx
85
+ J = dolfinx_adjoint.assemble_scalar(J_symbolic)
86
+
87
+ # Create a ReducedFunctional for optimization
88
+ control = pyadjoint.Control(f)
89
+ Jhat = pyadjoint.ReducedFunctional(J, control)
90
+
91
+ # Evaluate gradient
92
+ gradient = Jhat.derivative()
93
+ ```
94
+
95
+ <!-- #region -->
96
+ For more comprehensive examples, such as solving the optimal control of the Poisson equation or time-distributed control problems, check out the `demos/` directory or the [online documentation](https://scientificcomputing.github.io/dolfinx-adjoint).
97
+
98
+ ## Development and Testing
99
+
100
+ Code formatting is enforced via `ruff` and type-checking via `mypy`. To set up your local development environment:
101
+
102
+ ```bash
103
+ # Install development dependencies
104
+ python3 -m pip install -e ".[dev,test]"
105
+
106
+ # Run formatting checks
107
+ ruff check .
108
+ ruff format --check .
109
+
110
+ # Run type checking
111
+ python3 -m mypy .
112
+
113
+ # Run tests
114
+ python3 -m pytest -vs tests/
115
+ ```
116
+
117
+ ## License
118
+
119
+ MIT License. See [LICENSE](LICENSE) for more details.
120
+ <!-- #endregion -->
@@ -0,0 +1,111 @@
1
+ [build-system] # Require setuptool version due to https://github.com/pypa/setuptools/issues/2938
2
+ requires = ["setuptools>=61.0.0", "wheel"]
3
+
4
+ [project]
5
+ name = "dolfinx-adjoint"
6
+ version = "0.2.0"
7
+ description = "Automatic differentation compatible with DOLFINx"
8
+ authors = [{ name = "Jørgen S. Dokken", email = "dokken@simula.no" }]
9
+ license = "MIT"
10
+ license-files = ["LICENSE"]
11
+ readme = "README.md"
12
+ dependencies = [
13
+ "fenics-dolfinx>=0.10.0",
14
+ "pyadjoint-ad>=2025.10.0",
15
+ "typing_extensions; python_version < '3.11'",
16
+ ]
17
+
18
+
19
+ [project.optional-dependencies]
20
+ test = ["pytest"]
21
+ dev = ["pdbpp", "ipython", "mypy", "ruff"]
22
+ docs = [
23
+ "jupyter-book<2.0",
24
+ "jupytext",
25
+ # "moola@git+https://github.com/funsim/moola.git",
26
+ "pandas",
27
+ "pyvista[all]>0.45",
28
+ "networkx",
29
+ "pygraphviz"
30
+ ]
31
+ all = ["dolfinx_adjoint[test]", "dolfinx_adjoint[dev]", "dolfinx_adjoint[docs]"]
32
+
33
+ [tool.pytest.ini_options]
34
+ addopts = ["--import-mode=importlib"]
35
+ testpaths = ["tests"]
36
+
37
+ [tool.mypy]
38
+ ignore_missing_imports = true
39
+ # Folders to exclude
40
+ exclude = ["docs/", "build/"]
41
+ # Folder to check with mypy
42
+ files = ["src", "tests"]
43
+
44
+ [tool.ruff]
45
+ src = ["src", "docs", "tests", "examples"]
46
+ line-length = 120
47
+ indent-width = 4
48
+
49
+ [tool.ruff.lint]
50
+ select = [
51
+ # Pyflakes
52
+ "F",
53
+ # Pycodestyle
54
+ "E",
55
+ "W",
56
+ # isort
57
+ "I001",
58
+ ]
59
+
60
+
61
+ [tool.ruff.lint.isort]
62
+ known-first-party = ["dolfinx_adjoint"]
63
+ known-third-party = [
64
+ "basix",
65
+ "dolfinx",
66
+ "ffcx",
67
+ "ufl",
68
+ "gmsh",
69
+ "numpy",
70
+ "pytest",
71
+ "pyadjoint",
72
+ ]
73
+ section-order = [
74
+ "future",
75
+ "standard-library",
76
+ "mpi",
77
+ "third-party",
78
+ "first-party",
79
+ "local-folder",
80
+ ]
81
+
82
+ [tool.ruff.lint.isort.sections]
83
+ "mpi" = ["mpi4py", "petsc4py"]
84
+
85
+
86
+ [tool.bumpversion]
87
+ allow_dirty = false
88
+ commit = true
89
+ message = "Bump version: {current_version} → {new_version}"
90
+ tag = true
91
+ sign_tags = false
92
+ tag_name = "v{new_version}"
93
+ tag_message = "Bump version: {current_version} → {new_version}"
94
+ current_version = "0.2.0"
95
+
96
+
97
+ [[tool.bumpversion.files]]
98
+ filename = "pyproject.toml"
99
+ search = 'version = "{current_version}"'
100
+ replace = 'version = "{new_version}"'
101
+
102
+
103
+ [[tool.bumpversion.files]]
104
+ filename = "README.md"
105
+ search = "ghcr.io/scientificcomputing/dolfinx-adjoint:v{current_version}"
106
+ replace = "ghcr.io/scientificcomputing/dolfinx-adjoint:v{new_version}"
107
+
108
+ [[tool.bumpversion.files]]
109
+ filename = "Dockerfile"
110
+ search = "ghcr.io/scientificcomputing/dolfinx-adjoint:v{current_version}"
111
+ replace = "ghcr.io/scientificcomputing/dolfinx-adjoint:v{new_version}"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,37 @@
1
+ """Top-level package for dxa."""
2
+
3
+ from importlib.metadata import metadata
4
+
5
+ # Start annotation at import
6
+ import pyadjoint as _pyad
7
+
8
+ from .assembly import assemble_scalar, error_norm
9
+ from .solvers import LinearProblem, NonlinearProblem
10
+ from .types import Constant, Function
11
+ from .types.function import assign
12
+
13
+ meta = metadata("dolfinx_adjoint")
14
+ __version__ = meta.get("Version")
15
+ __license__ = meta.get("License")
16
+ __author__ = meta.get("Author")
17
+ __email__ = meta.get("Author-email")
18
+ __program_name__ = meta.get("Name")
19
+
20
+
21
+ _pyad.set_working_tape(_pyad.Tape())
22
+ _pyad.continue_annotation()
23
+
24
+ __all__ = [
25
+ "Constant",
26
+ "Function",
27
+ "LinearProblem",
28
+ "NonlinearProblem",
29
+ "assemble_scalar",
30
+ "assign",
31
+ "error_norm",
32
+ "__version__",
33
+ "__author__",
34
+ "__license__",
35
+ "__email__",
36
+ "__program_name__",
37
+ ]
@@ -0,0 +1,93 @@
1
+ import typing
2
+
3
+ from mpi4py import MPI
4
+
5
+ import dolfinx
6
+ import numpy
7
+ import numpy.typing as npt
8
+ import ufl
9
+ from pyadjoint.overloaded_type import create_overloaded_object
10
+ from pyadjoint.tape import annotate_tape, get_working_tape, stop_annotating
11
+
12
+ from .blocks.assembly import AssembleBlock
13
+
14
+
15
+ def assemble_scalar(form: ufl.Form, **kwargs):
16
+ """Assemble as scalar value from a form.
17
+
18
+ Args:
19
+ form: Symbolic form (UFL) to assemble.
20
+ kwargs: Keyword arguments to pass to the assembly routine.
21
+ Includes ``"ad_block_tag"`` to tag the block in the adjoint tape,
22
+ ``"annotate"`` to control whether the assembly is annotated in the adjoint tape,
23
+ ``"jit_options"`` for JIT compilation options,
24
+ and ``"form_compiler_options"`` for form compiler options and ``"entity_map"`` for assembling with Arguments
25
+ and coefficients form meshes that has some relation.
26
+ """
27
+ ad_block_tag = kwargs.pop("ad_block_tag", None)
28
+
29
+ annotate = annotate_tape(kwargs)
30
+ with stop_annotating():
31
+ compiled_form = dolfinx.fem.form(
32
+ form,
33
+ jit_options=kwargs.pop("jit_options", None),
34
+ form_compiler_options=kwargs.pop("form_compiler_options", None),
35
+ entity_maps=kwargs.pop("entity_maps", None),
36
+ )
37
+
38
+ local_output = dolfinx.fem.assemble_scalar(compiled_form)
39
+ comm = compiled_form.mesh.comm
40
+ output = comm.allreduce(local_output, op=MPI.SUM)
41
+ assert isinstance(output, float)
42
+
43
+ output = create_overloaded_object(output)
44
+
45
+ if annotate:
46
+ block = AssembleBlock(form, ad_block_tag=ad_block_tag)
47
+
48
+ tape = get_working_tape()
49
+ tape.add_block(block)
50
+
51
+ block.add_output(output.block_variable)
52
+
53
+ return output
54
+
55
+
56
+ def error_norm(
57
+ u_ex: ufl.core.expr.Expr,
58
+ u: ufl.core.expr.Expr,
59
+ norm_type=typing.Literal["L2", "H1"],
60
+ jit_options: typing.Optional[dict] = None,
61
+ form_compiler_options: typing.Optional[dict] = None,
62
+ entity_map: typing.Optional[dict[dolfinx.mesh.Mesh, npt.NDArray[numpy.int32]]] = None,
63
+ ad_block_tag: typing.Optional[str] = None,
64
+ annotate: bool = True,
65
+ ) -> float:
66
+ """Compute the error norm between the exact solution and the computed solution.
67
+
68
+ Args:
69
+ u_ex: The exact solution as a UFL expression.
70
+ u: The computed solution as a UFL expression.
71
+ norm_type: The type of norm to compute, either "L2" or "H1".
72
+ jit_options: Optional JIT compilation options.
73
+ form_compiler_options: Optional form compiler options.
74
+ entity_map: Optional mapping from mesh entities to submesh entities.
75
+ ad_block_tag: Optional tag for the block in the adjoint tape.
76
+ annotate: Whether to annotate the assignment in the adjoint tape.
77
+ Returns:
78
+ The computed error norm as a float.
79
+ """
80
+ diff = u_ex - u
81
+ norm = ufl.inner(diff, diff) * ufl.dx
82
+ if norm_type == "H1":
83
+ norm += ufl.inner(ufl.grad(diff), ufl.grad(diff)) * ufl.dx
84
+ return numpy.sqrt(
85
+ assemble_scalar(
86
+ norm,
87
+ jit_options=jit_options,
88
+ form_compiler_options=form_compiler_options,
89
+ entity_maps=entity_map,
90
+ ad_block_tag=ad_block_tag,
91
+ annotate=annotate,
92
+ )
93
+ )
@@ -0,0 +1,7 @@
1
+ from .assembly import AssembleBlock
2
+ from .function_assigner import FunctionAssignBlock
3
+
4
+ __all__ = [
5
+ "AssembleBlock",
6
+ "FunctionAssignBlock",
7
+ ]
@@ -0,0 +1,77 @@
1
+ import dolfinx
2
+ import numpy as np
3
+ import numpy.typing as npt
4
+
5
+
6
+ class _SpecialVector(dolfinx.la.Vector):
7
+ """Workaround adding __iadd__ to `dolfinx.la.Vector`."""
8
+
9
+ def __init__(self, x, function_space: dolfinx.fem.FunctionSpace):
10
+ super().__init__(x)
11
+ self._function_space = function_space
12
+
13
+ def __iadd__(self, other):
14
+ self.array[:] += other.array[:]
15
+ return self
16
+
17
+ @property
18
+ def function_space(self) -> dolfinx.fem.FunctionSpace:
19
+ return self._function_space
20
+
21
+ @property
22
+ def x(self):
23
+ return self
24
+
25
+ @property
26
+ def name(self):
27
+ return "SpecialVector"
28
+
29
+
30
+ def _vector(
31
+ map, bs: int, function_space: dolfinx.fem.FunctionSpace, dtype: npt.DTypeLike = np.float64
32
+ ) -> _SpecialVector:
33
+ """Create a distributed vector.
34
+
35
+ Args:
36
+ map: Index map the describes the size and distribution of the
37
+ vector.
38
+ bs: Block size.
39
+ dtype: The scalar type.
40
+
41
+ Returns:
42
+ A distributed vector.
43
+ """
44
+ if np.issubdtype(dtype, np.float32):
45
+ vtype = dolfinx.cpp.la.Vector_float32
46
+ elif np.issubdtype(dtype, np.float64):
47
+ vtype = dolfinx.cpp.la.Vector_float64
48
+ elif np.issubdtype(dtype, np.complex64):
49
+ vtype = dolfinx.cpp.la.Vector_complex64
50
+ elif np.issubdtype(dtype, np.complex128):
51
+ vtype = dolfinx.cpp.la.Vector_complex128
52
+ elif np.issubdtype(dtype, np.int8):
53
+ vtype = dolfinx.cpp.la.Vector_int8
54
+ elif np.issubdtype(dtype, np.int32):
55
+ vtype = dolfinx.cpp.la.Vector_int32
56
+ elif np.issubdtype(dtype, np.int64):
57
+ vtype = dolfinx.cpp.la.Vector_int64
58
+ else:
59
+ raise NotImplementedError(f"Type {dtype} not supported.")
60
+
61
+ return _SpecialVector(vtype(map, bs), function_space)
62
+
63
+
64
+ def _create_vector(L: dolfinx.fem.Form, space: dolfinx.fem.FunctionSpace) -> _SpecialVector:
65
+ """Create a Vector that is compatible with a given linear form.
66
+
67
+ Args:
68
+ L: A linear form.
69
+
70
+ Returns:
71
+ A vector that the form can be assembled into.
72
+ """
73
+ # Can just take the first dofmap here, since all dof maps have the same
74
+ # index map in mixed-topology meshes
75
+ dofmap = L.function_spaces[0].dofmaps(0) # type: ignore
76
+ assert space._cpp_object == L.function_spaces[0], "Function space mismatch when creating vector."
77
+ return _vector(dofmap.index_map, dofmap.index_map_bs, dtype=L.dtype, function_space=space)