diff --git a/.dockerignore b/.dockerignore index 821c19d..26f5c15 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1 +1 @@ -.github \ No newline at end of file +.github diff --git a/.github/workflows/build_docs.yml b/.github/workflows/build_docs.yml index 47c22b6..8969a94 100644 --- a/.github/workflows/build_docs.yml +++ b/.github/workflows/build_docs.yml @@ -26,17 +26,19 @@ jobs: - name: Install graphviz run: apt update && apt install -y graphviz-dev - + - name: Install mesa-utils - run: apt-get update && apt-get install -y mesa-utils - + run: apt-get update && apt-get install -y mesa-utils + - name: Install dependencies - run: python3 -m pip install ".[docs]" + run: | + python3 -m pip install git+https://github.com/funsim/moola.git + python3 -m pip install ".[docs]" - name: Build docs run: jupyter book build -W . - - name: Upload artifact + - name: Upload artifact uses: actions/upload-artifact@v7 # always upload artifact, which can include error messages if: always() diff --git a/.github/workflows/check_formatting.yml b/.github/workflows/check_formatting.yml index ce9fc6d..29ac985 100644 --- a/.github/workflows/check_formatting.yml +++ b/.github/workflows/check_formatting.yml @@ -32,6 +32,6 @@ jobs: run: | ruff check . ruff format --check . - + - name: Mypy check run: python3 -m mypy . diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index a31de8e..fa60b02 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -15,7 +15,7 @@ concurrency: group: "pages" cancel-in-progress: true -jobs: +jobs: check-formatting: uses: ./.github/workflows/check_formatting.yml @@ -48,7 +48,7 @@ jobs: # with: # name: code-coverage-report # path: "./public/code-coverage-report" - + - name: Upload artifact uses: actions/upload-pages-artifact@v5 with: diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml new file mode 100644 index 0000000..2a7f848 --- /dev/null +++ b/.github/workflows/pypi.yml @@ -0,0 +1,44 @@ +name: PyPI + +on: + push: + branches: + - "main" + tags: + - "v*" + pull_request: + branches: + - "main" + + +jobs: + dist: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Build SDist and wheel + run: pipx run build + + - uses: actions/upload-artifact@v7 + with: + path: dist/* + + - name: Check metadata + run: pipx run twine check dist/* + + publish: + needs: [dist] + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags') + environment: pypi + permissions: + id-token: write + + steps: + - uses: actions/download-artifact@v8 + with: + name: artifact + path: dist + + - uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/test_package.yml b/.github/workflows/test_package.yml index bcec261..b1217e3 100644 --- a/.github/workflows/test_package.yml +++ b/.github/workflows/test_package.yml @@ -4,11 +4,11 @@ on: push: branches: - main - + pull_request: branches: - main - + workflow_dispatch: workflow_call: @@ -28,4 +28,4 @@ jobs: - name: Run tests parallel - run: mpirun -n 2 python3 -m pytest -vs tests/ \ No newline at end of file + run: mpirun -n 2 python3 -m pytest -vs tests/ diff --git a/.gitignore b/.gitignore index 637c030..0e95bcd 100644 --- a/.gitignore +++ b/.gitignore @@ -131,4 +131,4 @@ dmypy.json _build/ *.dot -*.bp \ No newline at end of file +*.bp diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..39c107b --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,31 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + args: ['--maxkb=3000'] + - id: check-docstring-first + - id: debug-statements + - id: check-toml + + - repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: 'v0.15.14' + hooks: + # Run the linter. + - id: ruff + args: [ --fix ] + # Run the formatter. + - id: ruff-format + + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v2.1.0 + hooks: + - id: mypy + files: ^src/|^tests/ + args: ["--config-file", "pyproject.toml"] diff --git a/CITATION.cff b/CITATION.cff index 0657a5d..45d8082 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -10,8 +10,8 @@ authors: email: dokken@simula.no affiliation: Simula Research Laboratory orcid: 'https://orcid.org/0000-0001-6489-8858' -repository-code: 'https://github.com/jorgensd/dxa' -url: 'https://jsdokken.com/dxa' +repository-code: 'https://github.com/scientificcomputing/dolfinx-adjoint' +url: 'https://github.com/scientificcomputing/dolfinx-adjoint' abstract: >- Adjoint framework for DOLFINx keywords: @@ -21,4 +21,4 @@ keywords: - Optimization license: MIT version: v0.1.0 -date-released: '2023-04-04' \ No newline at end of file +date-released: '2023-04-04' diff --git a/Dockerfile b/Dockerfile index 4babb99..de21530 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Use github pages for docker image -FROM ghcr.io/jorgensd/dxa:v0.1.0 +FROM ghcr.io/scientificcomputing/dolfinx-adjoint:v0.2.0 # Create user with a home directory ARG NB_USER diff --git a/README.md b/README.md index ff6958a..f0ea7ad 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,120 @@ -# dxa -_________________ +# DOLFINx-Adjoint -[![MIT](https://img.shields.io/github/license/jorgensd/dxa)](LICENSE) -[Read Latest Documentation](https://jorgensd.github.io/dxa/) -_________________ +**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. + +Read the [Latest Documentation here](https://scientificcomputing.github.io/dolfinx-adjoint). + +> **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. + +## Features + +* **Automated Adjoints:** Seamlessly derive discrete adjoint models from DOLFINx forward models. + +* **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. + +* **Optimization:** Seamless integration with PDE-constrained optimization frameworks like [Moola](https://github.com/funsim/moola) or SciPy via `pyadjoint.ReducedFunctional`. + +## Installation + +### Dependencies + +DOLFINx-Adjoint requires **DOLFINx** (>=0.10.0) and **pyadjoint-ad**. + +### via pip + +The main way to install the package is via pip: + +```bash +python3 -m pip install dolfinx-adjoint +``` + +### Development Install + +To install the latest development version directly from the repository, use: + +```bash +python3 -m pip install git+[https://github.com/scientificcomputing/dolfinx-adjoint.git](https://github.com/scientificcomputing/dolfinx-adjoint.git) +``` + +If you plan to actively modify the code, clone the repository and install the optional dependencies for testing, development, and documentation generation: + +```bash +git clone [https://github.com/scientificcomputing/dolfinx-adjoint.git](https://github.com/scientificcomputing/dolfinx-adjoint.git) +cd dolfinx-adjoint +python3 -m pip install -e ".[all]" +``` + +### Docker + +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: + +```bash +docker run -ti ghcr.io/scientificcomputing/dolfinx-adjoint:v0.2.0 +``` + +*(Note: Adjust the tag to the latest release or build).* + +## Quick Start + +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: + + +```python +import dolfinx +from mpi4py import MPI +import pyadjoint +import dolfinx_adjoint + +# Create mesh and function space +mesh = dolfinx.mesh.create_unit_square(MPI.COMM_WORLD, 10, 10) +V = dolfinx.fem.functionspace(mesh, ("Lagrange", 1)) + +# Use dolfinx_adjoint overloaded types +# This ensures operations are tracked on the pyadjoint tape! +f = dolfinx_adjoint.Function(V, name="Control") +uh = dolfinx_adjoint.Function(V, name="State") + +# ... Define your UFL forms ... + +# Use overloaded solvers +problem = dolfinx_adjoint.LinearProblem(a, L, u=uh, bcs=[bc]) +problem.solve() + +# Assemble the objective scalar using the overloaded assembly +J_symbolic = 0.5 * ufl.inner(uh - d, uh - d) * ufl.dx +J = dolfinx_adjoint.assemble_scalar(J_symbolic) + +# Create a ReducedFunctional for optimization +control = pyadjoint.Control(f) +Jhat = pyadjoint.ReducedFunctional(J, control) + +# Evaluate gradient +gradient = Jhat.derivative() +``` + + +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). + +## Development and Testing + +Code formatting is enforced via `ruff` and type-checking via `mypy`. To set up your local development environment: + +```bash +# Install development dependencies +python3 -m pip install -e ".[dev,test]" + +# Run formatting checks +ruff check . +ruff format --check . + +# Run type checking +python3 -m mypy . + +# Run tests +python3 -m pytest -vs tests/ +``` + +## License + +MIT License. See [LICENSE](LICENSE) for more details. + diff --git a/_config.yml b/_config.yml index 6f5c3f3..e198ed7 100644 --- a/_config.yml +++ b/_config.yml @@ -24,7 +24,7 @@ html: bibtex_bibfiles: - - bibliography.bib + - docs/bibliography.bib parse: @@ -46,4 +46,4 @@ sphinx: - 'sphinx.ext.autodoc' - 'sphinx.ext.napoleon' - 'sphinx.ext.viewcode' -exclude_patterns: ["README.md",".pytest_cache/*"] +exclude_patterns: [".pytest_cache/*"] diff --git a/_toc.yml b/_toc.yml index 2d3c948..1b26ed8 100644 --- a/_toc.yml +++ b/_toc.yml @@ -1,5 +1,5 @@ format: jb-book -root: index +root: README.md parts: - caption: Examples @@ -9,4 +9,4 @@ parts: - caption: Python API chapters: - file: "docs/api" - - file: "docs/api_blocks" \ No newline at end of file + - file: "docs/api_blocks" diff --git a/docker/Dockerfile b/docker/Dockerfile index 3384f6a..75443cc 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -4,8 +4,8 @@ ENV DEB_PYTHON_INSTALL_LAYOUT=deb_system WORKDIR /tmp/ -COPY . ./dxa -RUN python3 -m pip install ./dxa[all] +COPY . ./dolfinx-adjoint +RUN python3 -m pip install ./dolfinx-adjoint[all] RUN rm -rf /tmp diff --git a/docs/api.rst b/docs/api.rst index 27f2f1e..e4802b6 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -5,4 +5,3 @@ The following is the API of the functions and classes provided by the `dolfinx_a .. automodule:: dolfinx_adjoint :members: - \ No newline at end of file diff --git a/docs/api_blocks.rst b/docs/api_blocks.rst index aa008c5..a71cb8b 100644 --- a/docs/api_blocks.rst +++ b/docs/api_blocks.rst @@ -4,4 +4,4 @@ DOLFINx-adjoint Blocks API The following is the API of the blocks implemented in the DOLFINx-adjoint package, which is used within the `pyadjoint` framework. .. automodule:: dolfinx_adjoint.blocks - :members: \ No newline at end of file + :members: diff --git a/bibliography.bib b/docs/bibliography.bib similarity index 100% rename from bibliography.bib rename to docs/bibliography.bib diff --git a/index.md b/index.md deleted file mode 100644 index ba57af3..0000000 --- a/index.md +++ /dev/null @@ -1,7 +0,0 @@ -# dxa - -Welcome to the webpage of dxa - -## Contents -```{tableofcontents} -``` diff --git a/pyproject.toml b/pyproject.toml index 975f832..588b458 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,11 +2,12 @@ requires = ["setuptools>=61.0.0", "wheel"] [project] -name = "dolfinx_adjoint" +name = "dolfinx-adjoint" version = "0.2.0" description = "Automatic differentation compatible with DOLFINx" authors = [{ name = "Jørgen S. Dokken", email = "dokken@simula.no" }] -license = { file = "LICENSE" } +license = "MIT" +license-files = ["LICENSE"] readme = "README.md" dependencies = [ "fenics-dolfinx>=0.10.0", @@ -21,10 +22,10 @@ dev = ["pdbpp", "ipython", "mypy", "ruff"] docs = [ "jupyter-book<2.0", "jupytext", - "moola@git+https://github.com/funsim/moola.git", + # "moola@git+https://github.com/funsim/moola.git", "pandas", "pyvista[all]>0.45", - "networkx", + "networkx", "pygraphviz" ] all = ["dolfinx_adjoint[test]", "dolfinx_adjoint[dev]", "dolfinx_adjoint[docs]"] @@ -80,3 +81,31 @@ section-order = [ [tool.ruff.lint.isort.sections] "mpi" = ["mpi4py", "petsc4py"] + + +[tool.bumpversion] +allow_dirty = false +commit = true +message = "Bump version: {current_version} → {new_version}" +tag = true +sign_tags = false +tag_name = "v{new_version}" +tag_message = "Bump version: {current_version} → {new_version}" +current_version = "0.2.0" + + +[[tool.bumpversion.files]] +filename = "pyproject.toml" +search = 'version = "{current_version}"' +replace = 'version = "{new_version}"' + + +[[tool.bumpversion.files]] +filename = "README.md" +search = "ghcr.io/scientificcomputing/dolfinx-adjoint:v{current_version}" +replace = "ghcr.io/scientificcomputing/dolfinx-adjoint:v{new_version}" + +[[tool.bumpversion.files]] +filename = "Dockerfile" +search = "ghcr.io/scientificcomputing/dolfinx-adjoint:v{current_version}" +replace = "ghcr.io/scientificcomputing/dolfinx-adjoint:v{new_version}"