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.
- dolfinx_adjoint-0.2.0/LICENSE +7 -0
- dolfinx_adjoint-0.2.0/PKG-INFO +151 -0
- dolfinx_adjoint-0.2.0/README.md +120 -0
- dolfinx_adjoint-0.2.0/pyproject.toml +111 -0
- dolfinx_adjoint-0.2.0/setup.cfg +4 -0
- dolfinx_adjoint-0.2.0/src/dolfinx_adjoint/__init__.py +37 -0
- dolfinx_adjoint-0.2.0/src/dolfinx_adjoint/assembly.py +93 -0
- dolfinx_adjoint-0.2.0/src/dolfinx_adjoint/blocks/__init__.py +7 -0
- dolfinx_adjoint-0.2.0/src/dolfinx_adjoint/blocks/_vector.py +77 -0
- dolfinx_adjoint-0.2.0/src/dolfinx_adjoint/blocks/assembly.py +322 -0
- dolfinx_adjoint-0.2.0/src/dolfinx_adjoint/blocks/function_assigner.py +198 -0
- dolfinx_adjoint-0.2.0/src/dolfinx_adjoint/blocks/solvers.py +1152 -0
- dolfinx_adjoint-0.2.0/src/dolfinx_adjoint/petsc_utils.py +89 -0
- dolfinx_adjoint-0.2.0/src/dolfinx_adjoint/solvers.py +248 -0
- dolfinx_adjoint-0.2.0/src/dolfinx_adjoint/types/__init__.py +3 -0
- dolfinx_adjoint-0.2.0/src/dolfinx_adjoint/types/function.py +291 -0
- dolfinx_adjoint-0.2.0/src/dolfinx_adjoint/utils.py +46 -0
- dolfinx_adjoint-0.2.0/src/dolfinx_adjoint.egg-info/PKG-INFO +151 -0
- dolfinx_adjoint-0.2.0/src/dolfinx_adjoint.egg-info/SOURCES.txt +25 -0
- dolfinx_adjoint-0.2.0/src/dolfinx_adjoint.egg-info/dependency_links.txt +1 -0
- dolfinx_adjoint-0.2.0/src/dolfinx_adjoint.egg-info/requires.txt +27 -0
- dolfinx_adjoint-0.2.0/src/dolfinx_adjoint.egg-info/top_level.txt +1 -0
- dolfinx_adjoint-0.2.0/tests/test_assemble_scalar.py +91 -0
- dolfinx_adjoint-0.2.0/tests/test_assign.py +161 -0
- dolfinx_adjoint-0.2.0/tests/test_linear_solver.py +90 -0
- dolfinx_adjoint-0.2.0/tests/test_poisson_mother.py +273 -0
- 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,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,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)
|