From 11d97d38c3f05d7c4f74d427a5c518e6fa94c534 Mon Sep 17 00:00:00 2001 From: lmoresi Date: Tue, 13 Jan 2026 22:52:01 +1100 Subject: [PATCH 01/21] Add ReadTheDocs configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Required for ReadTheDocs to find build configuration. API documentation source is on uw3-release-candidate branch. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .readthedocs.yaml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .readthedocs.yaml diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 00000000..e86d06b2 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,25 @@ +# ReadTheDocs configuration for Underworld3 API documentation +# See https://docs.readthedocs.io/en/stable/config-file/v2.html + +version: 2 + +# Build settings +build: + os: ubuntu-22.04 + tools: + python: "3.12" + +# Sphinx configuration +sphinx: + configuration: docs/api/conf.py + builder: html + fail_on_warning: false + +# Python settings - install docs dependencies +python: + install: + - requirements: docs/api/requirements.txt + - method: pip + path: . + extra_requirements: + - docs From 2d1de50466c6fa228cc4096f787db5b88b5ff685 Mon Sep 17 00:00:00 2001 From: lmoresi Date: Tue, 13 Jan 2026 22:55:41 +1100 Subject: [PATCH 02/21] Add Sphinx API documentation for ReadTheDocs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - docs/api/conf.py with MyST, napoleon, autodoc mocking - API pages for meshing, discretisation, solvers, etc. - requirements.txt for ReadTheDocs dependencies 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- docs/api/.gitignore | 1 + docs/api/conf.py | 145 ++++++++++++++++++++++++++++++++ docs/api/constitutive_models.md | 54 ++++++++++++ docs/api/coordinates.md | 26 ++++++ docs/api/discretisation.md | 36 ++++++++ docs/api/index.md | 30 +++++++ docs/api/meshing.md | 54 ++++++++++++ docs/api/requirements.txt | 5 ++ docs/api/solvers.md | 88 +++++++++++++++++++ docs/api/systems_ddt.md | 44 ++++++++++ 10 files changed, 483 insertions(+) create mode 100644 docs/api/.gitignore create mode 100644 docs/api/conf.py create mode 100644 docs/api/constitutive_models.md create mode 100644 docs/api/coordinates.md create mode 100644 docs/api/discretisation.md create mode 100644 docs/api/index.md create mode 100644 docs/api/meshing.md create mode 100644 docs/api/requirements.txt create mode 100644 docs/api/solvers.md create mode 100644 docs/api/systems_ddt.md diff --git a/docs/api/.gitignore b/docs/api/.gitignore new file mode 100644 index 00000000..69fa449d --- /dev/null +++ b/docs/api/.gitignore @@ -0,0 +1 @@ +_build/ diff --git a/docs/api/conf.py b/docs/api/conf.py new file mode 100644 index 00000000..b495171e --- /dev/null +++ b/docs/api/conf.py @@ -0,0 +1,145 @@ +# Sphinx configuration for Underworld3 API Documentation +# Uses MyST Markdown for familiar syntax + +import os +import sys + +# Add underworld3 source to path +sys.path.insert(0, os.path.abspath('../../src')) + +# ============================================================================= +# Project Information +# ============================================================================= +project = 'Underworld3' +copyright = '2025, Underworld Team' +author = 'Underworld Team' +version = '0.9' +release = '0.9b' + +# ============================================================================= +# Extensions +# NOTE: myst_nb includes myst_parser, don't include both! +# ============================================================================= +extensions = [ + 'myst_nb', # MyST Markdown + Notebooks (includes myst_parser) + 'sphinx.ext.autodoc', # Extract docstrings + 'sphinx.ext.napoleon', # NumPy/Google style docstrings + 'sphinx.ext.mathjax', # LaTeX math rendering + 'sphinx.ext.viewcode', # [source] links + 'sphinx.ext.intersphinx', # Link to external docs + 'sphinx_math_dollar', # $...$ math in docstrings +] + +# ============================================================================= +# MyST Configuration - familiar syntax from mystmd +# ============================================================================= +myst_enable_extensions = [ + "dollarmath", # $...$ and $$...$$ in .md files + "colon_fence", # ::: for directives + "deflist", # Definition lists + "fieldlist", # Field lists + "tasklist", # - [ ] task lists + "attrs_inline", # {#id .class} +] + +# Allow dollar signs for math (like mystmd) +myst_dmath_double_inline = True + +# Source file types +source_suffix = { + '.rst': 'restructuredtext', + '.md': 'myst-nb', + '.ipynb': 'myst-nb', +} + +# ============================================================================= +# Napoleon - NumPy docstrings +# ============================================================================= +napoleon_numpy_docstring = True +napoleon_google_docstring = False +napoleon_include_init_with_doc = True +napoleon_use_param = True +napoleon_use_rtype = True + +# ============================================================================= +# Autodoc +# ============================================================================= +autodoc_default_options = { + 'members': True, + 'member-order': 'bysource', + 'undoc-members': False, + 'show-inheritance': True, +} +autodoc_typehints = 'description' +autodoc_class_signature = 'separated' + +# Mock imports for modules that require PETSc/MPI (for ReadTheDocs builds) +# These modules will appear to import successfully but return empty mock objects +autodoc_mock_imports = [ + # Core dependencies that require compilation + 'petsc4py', + 'petsc4py.PETSc', + 'mpi4py', + 'mpi4py.MPI', + 'gmsh', + 'h5py', + 'pyvista', + # Cython-compiled underworld3 modules + 'underworld3.cython', + 'underworld3.cython.petsc_discretisation', + 'underworld3.cython.petsc_generic_snes_solvers', + 'underworld3.cython.petsc_maths', + 'underworld3.cython.generic_solvers', + 'underworld3.kdtree', +] + +# ============================================================================= +# Intersphinx - link to external docs +# ============================================================================= +intersphinx_mapping = { + 'python': ('https://docs.python.org/3', None), + 'numpy': ('https://numpy.org/doc/stable/', None), + 'sympy': ('https://docs.sympy.org/latest/', None), + 'petsc4py': ('https://petsc.org/release/petsc4py/', None), +} + +# ============================================================================= +# HTML - Furo theme +# ============================================================================= +html_theme = 'furo' +html_static_path = [] +html_title = "Underworld3 API" + +html_theme_options = { + "light_css_variables": { + "color-brand-primary": "#2962ff", + "color-brand-content": "#2962ff", + }, + "dark_css_variables": { + "color-brand-primary": "#5c8aff", + "color-brand-content": "#5c8aff", + }, +} + +# ============================================================================= +# Math - sphinx-math-dollar handles $...$ conversion, MathJax renders \(...\) +# ============================================================================= +mathjax3_config = { + 'tex': { + 'inlineMath': [['\\(', '\\)']], + 'displayMath': [['\\[', '\\]']], + } +} + +# ============================================================================= +# Other Settings +# ============================================================================= +# Don't fail on missing references during development +nitpicky = False +suppress_warnings = ['ref.python', 'myst.header'] + +# Don't execute notebooks (they may require PETSc/MPI) +nb_execution_mode = 'off' + +# Exclude patterns +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] diff --git a/docs/api/constitutive_models.md b/docs/api/constitutive_models.md new file mode 100644 index 00000000..dcc27bf8 --- /dev/null +++ b/docs/api/constitutive_models.md @@ -0,0 +1,54 @@ +# Constitutive Models + +```{eval-rst} +.. automodule:: underworld3.constitutive_models + :no-members: +``` + +## Viscous Flow Models + +### ViscousFlowModel + +```{eval-rst} +.. autoclass:: underworld3.constitutive_models.ViscousFlowModel + :members: + :show-inheritance: +``` + +## Diffusion Models + +### DiffusionModel + +```{eval-rst} +.. autoclass:: underworld3.constitutive_models.DiffusionModel + :members: + :show-inheritance: +``` + +### TransverseIsotropicFlowModel + +```{eval-rst} +.. autoclass:: underworld3.constitutive_models.TransverseIsotropicFlowModel + :members: + :show-inheritance: +``` + +## Viscoelastic Models + +### ViscoElasticPlasticFlowModel + +```{eval-rst} +.. autoclass:: underworld3.constitutive_models.ViscoElasticPlasticFlowModel + :members: + :show-inheritance: +``` + +## Darcy Flow + +### DarcyFlowModel + +```{eval-rst} +.. autoclass:: underworld3.constitutive_models.DarcyFlowModel + :members: + :show-inheritance: +``` diff --git a/docs/api/coordinates.md b/docs/api/coordinates.md new file mode 100644 index 00000000..39f26b92 --- /dev/null +++ b/docs/api/coordinates.md @@ -0,0 +1,26 @@ +# Coordinate Systems + +```{eval-rst} +.. automodule:: underworld3.coordinates + :no-members: +``` + +## CoordinateSystem + +The base class for coordinate system handling on meshes. + +```{eval-rst} +.. autoclass:: underworld3.coordinates.CoordinateSystem + :members: + :show-inheritance: +``` + +## CoordinateSystemType + +Enumeration of supported coordinate system types. + +```{eval-rst} +.. autoclass:: underworld3.coordinates.CoordinateSystemType + :members: + :show-inheritance: +``` diff --git a/docs/api/discretisation.md b/docs/api/discretisation.md new file mode 100644 index 00000000..8871177e --- /dev/null +++ b/docs/api/discretisation.md @@ -0,0 +1,36 @@ +# Discretisation + +```{eval-rst} +.. automodule:: underworld3.discretisation + :no-members: +``` + +## MeshVariable + +The primary class for field data on meshes. + +```{eval-rst} +.. autoclass:: underworld3.discretisation.MeshVariable + :members: + :show-inheritance: +``` + +## Swarm + +Particle swarms for Lagrangian tracking and material properties. + +```{eval-rst} +.. autoclass:: underworld3.swarm.Swarm + :members: + :show-inheritance: +``` + +## SwarmVariable + +Variables defined on particle swarms. + +```{eval-rst} +.. autoclass:: underworld3.swarm.SwarmVariable + :members: + :show-inheritance: +``` diff --git a/docs/api/index.md b/docs/api/index.md new file mode 100644 index 00000000..01589941 --- /dev/null +++ b/docs/api/index.md @@ -0,0 +1,30 @@ +# Underworld3 API Reference + +Underworld3 is a Python package for geodynamic modelling using PETSc finite elements. + +## Core Modules + +```{toctree} +:maxdepth: 2 + +meshing +discretisation +solvers +constitutive_models +coordinates +systems_ddt +``` + +## Quick Links + +- **{doc}`meshing`** - Create computational meshes (structured, unstructured, spherical) +- **{doc}`discretisation`** - Mesh variables and field data +- **{doc}`solvers`** - PDE solvers (Stokes, Poisson, advection-diffusion) +- **{doc}`constitutive_models`** - Material behaviour models (viscosity, diffusivity) +- **{doc}`coordinates`** - Coordinate systems and transformations +- **{doc}`systems_ddt`** - Time derivative discretisation + +## Indices + +* {ref}`genindex` +* {ref}`modindex` diff --git a/docs/api/meshing.md b/docs/api/meshing.md new file mode 100644 index 00000000..3fe75169 --- /dev/null +++ b/docs/api/meshing.md @@ -0,0 +1,54 @@ +# Meshing + +```{eval-rst} +.. automodule:: underworld3.meshing + :no-members: +``` + +## Cartesian Meshes + +### StructuredQuadBox + +```{eval-rst} +.. autofunction:: underworld3.meshing.StructuredQuadBox +``` + +### UnstructuredSimplexBox + +```{eval-rst} +.. autofunction:: underworld3.meshing.UnstructuredSimplexBox +``` + +## Spherical Meshes + +### SphericalShell + +```{eval-rst} +.. autofunction:: underworld3.meshing.SphericalShell +``` + +### RegionalSphericalBox + +```{eval-rst} +.. autofunction:: underworld3.meshing.RegionalSphericalBox +``` + +### CubedSphere + +```{eval-rst} +.. autofunction:: underworld3.meshing.CubedSphere +``` + +## Annulus Meshes + +### Annulus + +```{eval-rst} +.. autofunction:: underworld3.meshing.Annulus +``` + +### QuarterAnnulus + +```{eval-rst} +.. autofunction:: underworld3.meshing.QuarterAnnulus +``` diff --git a/docs/api/requirements.txt b/docs/api/requirements.txt new file mode 100644 index 00000000..63e5556f --- /dev/null +++ b/docs/api/requirements.txt @@ -0,0 +1,5 @@ +# Sphinx documentation dependencies +sphinx>=7.0 +furo +myst-nb>=1.0 +sphinx-math-dollar diff --git a/docs/api/solvers.md b/docs/api/solvers.md new file mode 100644 index 00000000..ad6add24 --- /dev/null +++ b/docs/api/solvers.md @@ -0,0 +1,88 @@ +# Solvers + +```{eval-rst} +.. automodule:: underworld3.systems.solvers + :no-members: +``` + +## Stokes Flow + +### SNES_Stokes + +```{eval-rst} +.. autoclass:: underworld3.systems.solvers.SNES_Stokes + :members: + :show-inheritance: +``` + +### SNES_VE_Stokes + +Viscoelastic extension of the Stokes solver. + +```{eval-rst} +.. autoclass:: underworld3.systems.solvers.SNES_VE_Stokes + :members: + :show-inheritance: +``` + +## Scalar Equations + +### SNES_Poisson + +```{eval-rst} +.. autoclass:: underworld3.systems.solvers.SNES_Poisson + :members: + :show-inheritance: +``` + +### SNES_Darcy + +```{eval-rst} +.. autoclass:: underworld3.systems.solvers.SNES_Darcy + :members: + :show-inheritance: +``` + +### SNES_AdvectionDiffusion + +```{eval-rst} +.. autoclass:: underworld3.systems.solvers.SNES_AdvectionDiffusion + :members: + :show-inheritance: +``` + +### SNES_Diffusion + +```{eval-rst} +.. autoclass:: underworld3.systems.solvers.SNES_Diffusion + :members: + :show-inheritance: +``` + +## Projection Solvers + +### SNES_Projection + +```{eval-rst} +.. autoclass:: underworld3.systems.solvers.SNES_Projection + :members: + :show-inheritance: +``` + +### SNES_Vector_Projection + +```{eval-rst} +.. autoclass:: underworld3.systems.solvers.SNES_Vector_Projection + :members: + :show-inheritance: +``` + +## Navier-Stokes + +### SNES_NavierStokes + +```{eval-rst} +.. autoclass:: underworld3.systems.solvers.SNES_NavierStokes + :members: + :show-inheritance: +``` diff --git a/docs/api/systems_ddt.md b/docs/api/systems_ddt.md new file mode 100644 index 00000000..5540f48a --- /dev/null +++ b/docs/api/systems_ddt.md @@ -0,0 +1,44 @@ +# Time Derivatives + +```{eval-rst} +.. automodule:: underworld3.systems.ddt + :no-members: +``` + +## Lagrangian Derivatives + +### Lagrangian_DDt + +```{eval-rst} +.. autoclass:: underworld3.systems.ddt.Lagrangian_DDt + :members: + :show-inheritance: +``` + +### SemiLagrangian_DDt + +```{eval-rst} +.. autoclass:: underworld3.systems.ddt.SemiLagrangian_DDt + :members: + :show-inheritance: +``` + +## Eulerian Derivatives + +### Eulerian_DDt + +```{eval-rst} +.. autoclass:: underworld3.systems.ddt.Eulerian_DDt + :members: + :show-inheritance: +``` + +## Flux History (Viscoelastic) + +### Lagrangian_Flux_DDt + +```{eval-rst} +.. autoclass:: underworld3.systems.ddt.Lagrangian_Flux_DDt + :members: + :show-inheritance: +``` From f02354748d46f756ce938034caaed6f88d7f2b90 Mon Sep 17 00:00:00 2001 From: lmoresi Date: Tue, 13 Jan 2026 23:03:10 +1100 Subject: [PATCH 03/21] Fix ReadTheDocs config - don't install underworld3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove pip install of underworld3 package - we use autodoc mocking so only Sphinx dependencies are needed, not the full package. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .readthedocs.yaml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index e86d06b2..083c2248 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -15,11 +15,8 @@ sphinx: builder: html fail_on_warning: false -# Python settings - install docs dependencies +# Python settings - only install Sphinx dependencies (not underworld3) +# We use autodoc mocking for PETSc/MPI dependencies python: install: - requirements: docs/api/requirements.txt - - method: pip - path: . - extra_requirements: - - docs From 35c5226542e9efd1fa819d73fb83d916bd7bb291 Mon Sep 17 00:00:00 2001 From: lmoresi Date: Wed, 14 Jan 2026 06:33:32 +1100 Subject: [PATCH 04/21] Use pixi-based build for ReadTheDocs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch from autodoc mocking to full pixi environment with conda-forge PETSc. This allows proper documentation generation with real imports. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .readthedocs.yaml | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 083c2248..a3fbba9b 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -1,22 +1,27 @@ # ReadTheDocs configuration for Underworld3 API documentation -# See https://docs.readthedocs.io/en/stable/config-file/v2.html +# Uses pixi for conda-forge PETSc environment +# See https://docs.readthedocs.com/platform/stable/build-customization.html version: 2 -# Build settings build: os: ubuntu-22.04 tools: python: "3.12" + commands: + # Install pixi + - curl -fsSL https://pixi.sh/install.sh | bash + - export PATH="$HOME/.pixi/bin:$PATH" -# Sphinx configuration -sphinx: - configuration: docs/api/conf.py - builder: html - fail_on_warning: false + # Install the dev environment (includes Sphinx and PETSc) + - pixi install -e dev -# Python settings - only install Sphinx dependencies (not underworld3) -# We use autodoc mocking for PETSc/MPI dependencies -python: - install: - - requirements: docs/api/requirements.txt + # Build underworld3 + - pixi run -e dev build + + # Build Sphinx documentation + - pixi run -e dev api-docs-build + + # Move output to ReadTheDocs expected location + - mkdir -p $READTHEDOCS_OUTPUT/html + - cp -r docs/api/_build/html/* $READTHEDOCS_OUTPUT/html/ From e54df1bf7b318bf1b201be47c32b577a41150074 Mon Sep 17 00:00:00 2001 From: lmoresi Date: Wed, 14 Jan 2026 06:42:03 +1100 Subject: [PATCH 05/21] Fix pixi path in ReadTheDocs config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use full path $HOME/.pixi/bin/pixi since each command runs in a separate shell and PATH exports don't persist. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .readthedocs.yaml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index a3fbba9b..c9032ac7 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -11,17 +11,12 @@ build: commands: # Install pixi - curl -fsSL https://pixi.sh/install.sh | bash - - export PATH="$HOME/.pixi/bin:$PATH" - # Install the dev environment (includes Sphinx and PETSc) - - pixi install -e dev - + - $HOME/.pixi/bin/pixi install -e dev # Build underworld3 - - pixi run -e dev build - + - $HOME/.pixi/bin/pixi run -e dev build # Build Sphinx documentation - - pixi run -e dev api-docs-build - + - $HOME/.pixi/bin/pixi run -e dev api-docs-build # Move output to ReadTheDocs expected location - mkdir -p $READTHEDOCS_OUTPUT/html - cp -r docs/api/_build/html/* $READTHEDOCS_OUTPUT/html/ From 3110ea812c74fdebc6aa9e4638a10d4b618ed78a Mon Sep 17 00:00:00 2001 From: lmoresi Date: Wed, 14 Jan 2026 06:43:17 +1100 Subject: [PATCH 06/21] Use ReadTheDocs jobs pattern with asdf for pixi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow ReadTheDocs recommended pattern using jobs hooks and asdf to install pixi, rather than custom curl commands. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .readthedocs.yaml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index c9032ac7..75f1b4c9 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -5,18 +5,18 @@ version: 2 build: - os: ubuntu-22.04 + os: ubuntu-24.04 tools: - python: "3.12" - commands: - # Install pixi - - curl -fsSL https://pixi.sh/install.sh | bash - # Install the dev environment (includes Sphinx and PETSc) - - $HOME/.pixi/bin/pixi install -e dev - # Build underworld3 - - $HOME/.pixi/bin/pixi run -e dev build - # Build Sphinx documentation - - $HOME/.pixi/bin/pixi run -e dev api-docs-build - # Move output to ReadTheDocs expected location - - mkdir -p $READTHEDOCS_OUTPUT/html - - cp -r docs/api/_build/html/* $READTHEDOCS_OUTPUT/html/ + python: "latest" + jobs: + create_environment: + - asdf plugin add pixi + - asdf install pixi latest + - asdf global pixi latest + install: + - pixi install -e dev + - pixi run -e dev build + build: + html: + - pixi run -e dev api-docs-build + - cp -r docs/api/_build/html/* $READTHEDOCS_OUTPUT/html/ From 48d70884cb8e12886a99998e2ae386e6d3b7c360 Mon Sep 17 00:00:00 2001 From: lmoresi Date: Wed, 14 Jan 2026 07:26:38 +1100 Subject: [PATCH 07/21] Create output directory before copying docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .readthedocs.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 75f1b4c9..7db4561c 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -19,4 +19,5 @@ build: build: html: - pixi run -e dev api-docs-build + - mkdir -p $READTHEDOCS_OUTPUT/html - cp -r docs/api/_build/html/* $READTHEDOCS_OUTPUT/html/ From 31e27745a200a724d449e0e85f7bcc95b6e9e242 Mon Sep 17 00:00:00 2001 From: lmoresi Date: Wed, 14 Jan 2026 08:33:28 +1100 Subject: [PATCH 08/21] Remove autodoc_mock_imports - pixi provides real PETSc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With pixi-based builds on ReadTheDocs, we have a real PETSc environment so mocking is no longer needed and was actually interfering with Cython module introspection. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- docs/api/conf.py | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/docs/api/conf.py b/docs/api/conf.py index b495171e..f3b6edc5 100644 --- a/docs/api/conf.py +++ b/docs/api/conf.py @@ -73,26 +73,6 @@ autodoc_typehints = 'description' autodoc_class_signature = 'separated' -# Mock imports for modules that require PETSc/MPI (for ReadTheDocs builds) -# These modules will appear to import successfully but return empty mock objects -autodoc_mock_imports = [ - # Core dependencies that require compilation - 'petsc4py', - 'petsc4py.PETSc', - 'mpi4py', - 'mpi4py.MPI', - 'gmsh', - 'h5py', - 'pyvista', - # Cython-compiled underworld3 modules - 'underworld3.cython', - 'underworld3.cython.petsc_discretisation', - 'underworld3.cython.petsc_generic_snes_solvers', - 'underworld3.cython.petsc_maths', - 'underworld3.cython.generic_solvers', - 'underworld3.kdtree', -] - # ============================================================================= # Intersphinx - link to external docs # ============================================================================= From 646601958c5283d2f4ae973c04786e769512ed86 Mon Sep 17 00:00:00 2001 From: lmoresi Date: Wed, 14 Jan 2026 12:13:22 +1100 Subject: [PATCH 09/21] Fix circular import: remove sys.path to source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sphinx was importing from source instead of installed package, causing circular import in function/__init__.py -> analytic. The package is already installed by 'pixi run build' before docs build. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- docs/api/conf.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/docs/api/conf.py b/docs/api/conf.py index f3b6edc5..be136060 100644 --- a/docs/api/conf.py +++ b/docs/api/conf.py @@ -1,11 +1,7 @@ # Sphinx configuration for Underworld3 API Documentation # Uses MyST Markdown for familiar syntax - -import os -import sys - -# Add underworld3 source to path -sys.path.insert(0, os.path.abspath('../../src')) +# NOTE: underworld3 is installed via 'pixi run build' before docs build +# Do NOT add source to sys.path - it causes circular import issues # ============================================================================= # Project Information From b9cb199aeceab327e0389f3162ad92f5f725e4a2 Mon Sep 17 00:00:00 2001 From: lmoresi Date: Wed, 14 Jan 2026 12:57:34 +1100 Subject: [PATCH 10/21] Add documentation for missing modules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added API docs for: - function: Expressions, quantities, evaluation - swarm: Particle swarms and variables - scaling: Units, quantities, non-dimensionalisation - maths: Mathematical operations and integrals - materials: Multi-material systems - model: Model management and configuration - utilities: I/O and helper functions - visualisation: Plotting tools - adaptivity: Adaptive mesh refinement 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- docs/api/adaptivity.md | 13 ++++++++ docs/api/function.md | 44 ++++++++++++++++++++++++++ docs/api/index.md | 34 +++++++++++++++++++- docs/api/materials.md | 30 ++++++++++++++++++ docs/api/maths.md | 21 +++++++++++++ docs/api/model.md | 40 ++++++++++++++++++++++++ docs/api/scaling.md | 66 +++++++++++++++++++++++++++++++++++++++ docs/api/swarm.md | 38 ++++++++++++++++++++++ docs/api/utilities.md | 53 +++++++++++++++++++++++++++++++ docs/api/visualisation.md | 25 +++++++++++++++ 10 files changed, 363 insertions(+), 1 deletion(-) create mode 100644 docs/api/adaptivity.md create mode 100644 docs/api/function.md create mode 100644 docs/api/materials.md create mode 100644 docs/api/maths.md create mode 100644 docs/api/model.md create mode 100644 docs/api/scaling.md create mode 100644 docs/api/swarm.md create mode 100644 docs/api/utilities.md create mode 100644 docs/api/visualisation.md diff --git a/docs/api/adaptivity.md b/docs/api/adaptivity.md new file mode 100644 index 00000000..223e5666 --- /dev/null +++ b/docs/api/adaptivity.md @@ -0,0 +1,13 @@ +# Adaptive Mesh Refinement + +```{eval-rst} +.. automodule:: underworld3.adaptivity + :members: + :show-inheritance: +``` + +## Mesh Adaptation + +Tools for adaptive mesh refinement (AMR) in Underworld3. + +**Note**: AMR features require the `amr` environment with custom PETSc build. diff --git a/docs/api/function.md b/docs/api/function.md new file mode 100644 index 00000000..a3b2d938 --- /dev/null +++ b/docs/api/function.md @@ -0,0 +1,44 @@ +# Function and Expressions + +```{eval-rst} +.. automodule:: underworld3.function + :no-members: +``` + +## Expressions + +### UWexpression + +```{eval-rst} +.. autoclass:: underworld3.function.UWexpression + :members: + :show-inheritance: +``` + +## Quantities and Units + +### UWQuantity + +```{eval-rst} +.. autoclass:: underworld3.function.UWQuantity + :members: + :show-inheritance: +``` + +## Evaluation + +```{eval-rst} +.. autofunction:: underworld3.function.evaluate +``` + +```{eval-rst} +.. autofunction:: underworld3.function.evalf +``` + +## Analytic Functions + +```{eval-rst} +.. automodule:: underworld3.function.analytic + :members: + :show-inheritance: +``` diff --git a/docs/api/index.md b/docs/api/index.md index 01589941..ab7ac16f 100644 --- a/docs/api/index.md +++ b/docs/api/index.md @@ -9,20 +9,52 @@ Underworld3 is a Python package for geodynamic modelling using PETSc finite elem meshing discretisation +swarm solvers constitutive_models +function +scaling +``` + +## Supporting Modules + +```{toctree} +:maxdepth: 2 + coordinates systems_ddt +maths +materials +model +utilities +visualisation +adaptivity ``` ## Quick Links +### Mesh and Variables - **{doc}`meshing`** - Create computational meshes (structured, unstructured, spherical) - **{doc}`discretisation`** - Mesh variables and field data +- **{doc}`swarm`** - Particle swarms and Lagrangian tracking +- **{doc}`coordinates`** - Coordinate systems and transformations + +### Solvers and Physics - **{doc}`solvers`** - PDE solvers (Stokes, Poisson, advection-diffusion) - **{doc}`constitutive_models`** - Material behaviour models (viscosity, diffusivity) -- **{doc}`coordinates`** - Coordinate systems and transformations - **{doc}`systems_ddt`** - Time derivative discretisation +- **{doc}`materials`** - Multi-material systems + +### Functions and Units +- **{doc}`function`** - Expressions, evaluation, and symbolic functions +- **{doc}`scaling`** - Units, quantities, and non-dimensionalisation +- **{doc}`maths`** - Mathematical operations and integrals + +### Infrastructure +- **{doc}`model`** - Model management and configuration +- **{doc}`utilities`** - I/O, mesh import, and helper functions +- **{doc}`visualisation`** - Plotting and visualisation tools +- **{doc}`adaptivity`** - Adaptive mesh refinement (AMR) ## Indices diff --git a/docs/api/materials.md b/docs/api/materials.md new file mode 100644 index 00000000..ad8476be --- /dev/null +++ b/docs/api/materials.md @@ -0,0 +1,30 @@ +# Materials + +```{eval-rst} +.. automodule:: underworld3.materials + :no-members: +``` + +## Material Property + +```{eval-rst} +.. autoclass:: underworld3.MaterialProperty + :members: + :show-inheritance: +``` + +## Material Registry + +```{eval-rst} +.. autoclass:: underworld3.MaterialRegistry + :members: + :show-inheritance: +``` + +## Multi-Material Models + +```{eval-rst} +.. autoclass:: underworld3.MultiMaterialConstitutiveModel + :members: + :show-inheritance: +``` diff --git a/docs/api/maths.md b/docs/api/maths.md new file mode 100644 index 00000000..106e45ba --- /dev/null +++ b/docs/api/maths.md @@ -0,0 +1,21 @@ +# Mathematical Operations + +```{eval-rst} +.. automodule:: underworld3.maths + :members: + :show-inheritance: +``` + +## Integral Operations + +```{eval-rst} +.. autoclass:: underworld3.maths.Integral + :members: + :show-inheritance: +``` + +## Vector Calculus + +```{eval-rst} +.. autofunction:: underworld3.maths.delta_function +``` diff --git a/docs/api/model.md b/docs/api/model.md new file mode 100644 index 00000000..a7030207 --- /dev/null +++ b/docs/api/model.md @@ -0,0 +1,40 @@ +# Model Management + +```{eval-rst} +.. automodule:: underworld3.model + :no-members: +``` + +## Model Class + +```{eval-rst} +.. autoclass:: underworld3.Model + :members: + :show-inheritance: +``` + +## Model Functions + +```{eval-rst} +.. autofunction:: underworld3.get_default_model +``` + +```{eval-rst} +.. autofunction:: underworld3.reset_default_model +``` + +```{eval-rst} +.. autofunction:: underworld3.create_model +``` + +## Thermal Convection Configuration + +```{eval-rst} +.. autoclass:: underworld3.ThermalConvectionConfig + :members: + :show-inheritance: +``` + +```{eval-rst} +.. autofunction:: underworld3.create_thermal_convection_model +``` diff --git a/docs/api/scaling.md b/docs/api/scaling.md new file mode 100644 index 00000000..7572b982 --- /dev/null +++ b/docs/api/scaling.md @@ -0,0 +1,66 @@ +# Units and Scaling + +```{eval-rst} +.. automodule:: underworld3.scaling + :no-members: +``` + +## Creating Quantities + +```{eval-rst} +.. autofunction:: underworld3.quantity +``` + +```{eval-rst} +.. autofunction:: underworld3.expression +``` + +## Unit Conversion + +```{eval-rst} +.. autofunction:: underworld3.get_units +``` + +```{eval-rst} +.. autofunction:: underworld3.convert_units +``` + +```{eval-rst} +.. autofunction:: underworld3.to_base_units +``` + +```{eval-rst} +.. autofunction:: underworld3.to_compact +``` + +## Non-dimensionalisation + +```{eval-rst} +.. autofunction:: underworld3.non_dimensionalise +``` + +```{eval-rst} +.. autofunction:: underworld3.dimensionalise +``` + +```{eval-rst} +.. autofunction:: underworld3.is_nondimensional_scaling_active +``` + +## Dimensional Analysis + +```{eval-rst} +.. autofunction:: underworld3.get_dimensionality +``` + +```{eval-rst} +.. autofunction:: underworld3.is_dimensionless +``` + +```{eval-rst} +.. autofunction:: underworld3.has_units +``` + +```{eval-rst} +.. autofunction:: underworld3.same_units +``` diff --git a/docs/api/swarm.md b/docs/api/swarm.md new file mode 100644 index 00000000..df811d08 --- /dev/null +++ b/docs/api/swarm.md @@ -0,0 +1,38 @@ +# Particle Swarms + +```{eval-rst} +.. automodule:: underworld3.swarm + :no-members: +``` + +## Swarm + +```{eval-rst} +.. autoclass:: underworld3.swarm.Swarm + :members: + :show-inheritance: +``` + +## SwarmVariable + +```{eval-rst} +.. autoclass:: underworld3.swarm.SwarmVariable + :members: + :show-inheritance: +``` + +## Population Control + +```{eval-rst} +.. autoclass:: underworld3.swarm.PopulationControl + :members: + :show-inheritance: +``` + +## Index Swarm Variable + +```{eval-rst} +.. autoclass:: underworld3.swarm.IndexSwarmVariable + :members: + :show-inheritance: +``` diff --git a/docs/api/utilities.md b/docs/api/utilities.md new file mode 100644 index 00000000..fadd4236 --- /dev/null +++ b/docs/api/utilities.md @@ -0,0 +1,53 @@ +# Utilities + +```{eval-rst} +.. automodule:: underworld3.utilities + :no-members: +``` + +## I/O Utilities + +### XDMF Generation + +```{eval-rst} +.. autofunction:: underworld3.utilities.generateXdmf +``` + +```{eval-rst} +.. autofunction:: underworld3.utilities.generate_uw_Xdmf +``` + +### Swarm I/O + +```{eval-rst} +.. autofunction:: underworld3.utilities.swarm_h5 +``` + +```{eval-rst} +.. autofunction:: underworld3.utilities.swarm_xdmf +``` + +## Mesh Import + +```{eval-rst} +.. autofunction:: underworld3.utilities.read_medit_ascii +``` + +```{eval-rst} +.. autofunction:: underworld3.utilities.create_dmplex_from_medit +``` + +## Development Utilities + +```{eval-rst} +.. autoclass:: underworld3.utilities.CaptureStdout + :members: +``` + +```{eval-rst} +.. autofunction:: underworld3.utilities.h5_scan +``` + +```{eval-rst} +.. autofunction:: underworld3.utilities.mem_footprint +``` diff --git a/docs/api/visualisation.md b/docs/api/visualisation.md new file mode 100644 index 00000000..636efc93 --- /dev/null +++ b/docs/api/visualisation.md @@ -0,0 +1,25 @@ +# Visualisation + +```{eval-rst} +.. automodule:: underworld3.visualisation + :members: + :show-inheritance: +``` + +## Mesh Visualisation + +Tools for visualising meshes, variables, and swarms using PyVista. + +## Plotting Utilities + +```{eval-rst} +.. autofunction:: underworld3.visualisation.plot_mesh +``` + +```{eval-rst} +.. autofunction:: underworld3.visualisation.plot_scalar +``` + +```{eval-rst} +.. autofunction:: underworld3.visualisation.plot_vector +``` From a6a5bb3d6985c418251d63d80da4ab01e28a5bbe Mon Sep 17 00:00:00 2001 From: lmoresi Date: Wed, 14 Jan 2026 16:36:16 +1100 Subject: [PATCH 11/21] Complete meshing docs and add global_evaluate --- docs/api/function.md | 16 +++++++ docs/api/meshing.md | 108 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 117 insertions(+), 7 deletions(-) diff --git a/docs/api/function.md b/docs/api/function.md index a3b2d938..6db01987 100644 --- a/docs/api/function.md +++ b/docs/api/function.md @@ -27,14 +27,30 @@ ## Evaluation +### evaluate + ```{eval-rst} .. autofunction:: underworld3.function.evaluate ``` +### global_evaluate + +```{eval-rst} +.. autofunction:: underworld3.function.global_evaluate +``` + +### evalf + ```{eval-rst} .. autofunction:: underworld3.function.evalf ``` +### evaluate_gradient + +```{eval-rst} +.. autofunction:: underworld3.function.evaluate_gradient +``` + ## Analytic Functions ```{eval-rst} diff --git a/docs/api/meshing.md b/docs/api/meshing.md index 3fe75169..c37db733 100644 --- a/docs/api/meshing.md +++ b/docs/api/meshing.md @@ -19,6 +19,50 @@ .. autofunction:: underworld3.meshing.UnstructuredSimplexBox ``` +### BoxInternalBoundary + +```{eval-rst} +.. autofunction:: underworld3.meshing.BoxInternalBoundary +``` + +## Annulus Meshes + +### Annulus + +```{eval-rst} +.. autofunction:: underworld3.meshing.Annulus +``` + +### QuarterAnnulus + +```{eval-rst} +.. autofunction:: underworld3.meshing.QuarterAnnulus +``` + +### SegmentofAnnulus + +```{eval-rst} +.. autofunction:: underworld3.meshing.SegmentofAnnulus +``` + +### AnnulusWithSpokes + +```{eval-rst} +.. autofunction:: underworld3.meshing.AnnulusWithSpokes +``` + +### AnnulusInternalBoundary + +```{eval-rst} +.. autofunction:: underworld3.meshing.AnnulusInternalBoundary +``` + +### DiscInternalBoundaries + +```{eval-rst} +.. autofunction:: underworld3.meshing.DiscInternalBoundaries +``` + ## Spherical Meshes ### SphericalShell @@ -27,28 +71,78 @@ .. autofunction:: underworld3.meshing.SphericalShell ``` +### CubedSphere + +```{eval-rst} +.. autofunction:: underworld3.meshing.CubedSphere +``` + +### SegmentofSphere + +```{eval-rst} +.. autofunction:: underworld3.meshing.SegmentofSphere +``` + +### SphericalShellInternalBoundary + +```{eval-rst} +.. autofunction:: underworld3.meshing.SphericalShellInternalBoundary +``` + +## Geographic Meshes + +Regional meshes defined using longitude/latitude coordinates. + ### RegionalSphericalBox ```{eval-rst} .. autofunction:: underworld3.meshing.RegionalSphericalBox ``` -### CubedSphere +### RegionalGeographicBox ```{eval-rst} -.. autofunction:: underworld3.meshing.CubedSphere +.. autofunction:: underworld3.meshing.RegionalGeographicBox ``` -## Annulus Meshes +## Segmented Meshes -### Annulus +Meshes with internal segment boundaries for multi-region problems. + +### SegmentedSphericalShell ```{eval-rst} -.. autofunction:: underworld3.meshing.Annulus +.. autofunction:: underworld3.meshing.SegmentedSphericalShell ``` -### QuarterAnnulus +### SegmentedSphericalSurface2D ```{eval-rst} -.. autofunction:: underworld3.meshing.QuarterAnnulus +.. autofunction:: underworld3.meshing.SegmentedSphericalSurface2D +``` + +### SegmentedSphericalBall + +```{eval-rst} +.. autofunction:: underworld3.meshing.SegmentedSphericalBall +``` + +## Faults and Internal Boundaries + +Tools for creating meshes with fault surfaces and internal boundaries. + +### FaultSurface + +```{eval-rst} +.. autoclass:: underworld3.meshing.FaultSurface + :members: + :show-inheritance: +``` + +### FaultCollection + +```{eval-rst} +.. autoclass:: underworld3.meshing.FaultCollection + :members: + :show-inheritance: ``` From bea24b9e1af820303c4603acc6c1f757357073aa Mon Sep 17 00:00:00 2001 From: lmoresi Date: Wed, 14 Jan 2026 17:18:10 +1100 Subject: [PATCH 12/21] Add docs-audit task to check documentation coverage --- pixi.toml | 215 ++++++++++++++++++++++++++++++++++++++++++ scripts/docs_audit.py | 198 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 413 insertions(+) create mode 100644 pixi.toml create mode 100644 scripts/docs_audit.py diff --git a/pixi.toml b/pixi.toml new file mode 100644 index 00000000..92dbe315 --- /dev/null +++ b/pixi.toml @@ -0,0 +1,215 @@ +# Underworld3 In-Repository Build Configuration +# +# Two PETSc options: +# - Default: conda-forge PETSc (~5 min install) +# - AMR: Custom PETSc with adaptive mesh tools (~1 hour build) +# +# Three environment tiers: +# - default/amr: Minimal - build and run underworld3 +# - runtime/amr-runtime: Run tutorials - adds viz, jupyter +# - dev/amr-dev: Development - adds claude, linting, etc. +# +# Usage: +# pixi install # Minimal environment +# pixi install -e runtime # For running tutorials +# pixi install -e dev # For development +# +# pixi install -e amr # AMR minimal (then: pixi run -e amr petsc-build) +# pixi install -e amr-runtime # AMR for tutorials +# pixi install -e amr-dev # AMR for development + +[workspace] +name = "underworld3" +authors = ["Louis Moresi "] +channels = ["conda-forge"] +platforms = ["osx-arm64", "linux-64"] +version = "0.99.0b" + +# ============================================ +# CORE TASKS +# ============================================ +[tasks] +# Build underworld3 (compiles Cython extensions) +build = "pip install . --no-build-isolation" +clean = "./scripts/clean.sh" + +# Testing +test = "./scripts/test_levels.sh 1" # Quick level-1 tests (~2 min) +test-all = "./scripts/test_levels.sh" # All test levels + +# Code quality +format = "black src/underworld3 tests --line-length=100" +type-check = "mypy src/underworld3 --ignore-missing-imports" + +# Optional installers for specialized tools (not bundled) +install-geo = "pip install gdal pyproj cartopy geopandas owslib" + +# ============================================ +# BASE DEPENDENCIES (shared by all environments) +# ============================================ +# NOTE: petsc/petsc4py are in features below, not here + +[dependencies] +python = "3.12.*" + +# Build requirements +cython = ">=3.1,<4" +numpy = "<2" +pip = ">=25,<26" +setuptools = ">=75,<76" +compilers = "*" + +# Underworld3 runtime dependencies +sympy = ">=1.13,<2" +scipy = ">=1.15,<2" +pint = ">=0.24,<0.25" +typeguard = ">=4.4,<5" +xxhash = ">=0.8,<0.9" + +# Testing (included in base for CI compatibility) +pytest = ">=8.3,<9" +pytest-mpi = ">=0.6,<0.7" +pytest-timeout = ">=2.3,<3" +pytest-forked = ">=1.6,<2" +pytest-xdist = ">=3.8,<4" + +# Mesh generation +pykdtree = ">=1.4,<2" +meshio = ">=5.3,<6" + +# Utilities +requests = "*" +matplotlib = ">=3.10,<4" + +# Jupyter kernel support (needed for pixi-kernel in all environments) +ipykernel = ">=6.29,<7" +pixi-kernel = ">=0.7.1,<0.8" + +[pypi-dependencies] +gmsh = ">=4.13,<5" +pygmsh = ">=7.1,<8" +rich = "*" + +# ============================================ +# CONDA-FORGE PETSC FEATURE +# ============================================ +# Quick install (~5 min), no AMR tools + +[feature.conda-petsc.dependencies] +# PETSc 3.24 compatible - requires clean rebuild after version changes +petsc = ">=3.21,<4" +petsc4py = ">=3.21,<4" +h5py = { version = ">=3.12,<4", build = "*mpich*" } +mpi4py = ">=4,<5" + +# ============================================ +# CUSTOM PETSC FEATURE (AMR) +# ============================================ +# Build PETSc from source with adaptive mesh tools: +# - pragmatic: anisotropic mesh adaptation +# - mmg/parmmg: surface/volume mesh adaptation +# - slepc: eigenvalue solvers +# Build time: ~1 hour on Apple Silicon + +[feature.amr.dependencies] +# Build tools for PETSc compilation +gfortran = ">=14.2,<15" +cxx-compiler = ">=1.9,<2" +cmake = ">=3.31,<4" +make = ">=4.4,<5" + +# MPI and HDF5 from conda-forge (PETSc links against these) +mpich = ">=4.3,<5" +mpi4py = ">=4,<5" +hdf5 = { version = ">=1.14,<2", build = "*mpich*" } +h5py = { version = ">=3.12,<4", build = "*mpich*" } + +# NOTE: petsc and petsc4py are built from source +# by petsc-custom/build-petsc.sh + +[feature.amr.activation.env] +PETSC_DIR = "$PIXI_PROJECT_ROOT/petsc-custom/petsc" +PETSC_ARCH = "petsc-4-uw" + +[feature.amr.tasks] +# Build custom PETSc with AMR tools (~1 hour, includes petsc4py) +# Sub-commands: ./build-petsc.sh [configure|build|petsc4py|clean|help] +petsc-local-build = { cmd = "./build-petsc.sh", cwd = "petsc-custom" } +petsc-local-clean = { cmd = "./build-petsc.sh clean", cwd = "petsc-custom" } + +# ============================================ +# RUNTIME FEATURE (for tutorials/examples) +# ============================================ +# Visualization and interactive computing + +[feature.runtime.dependencies] +# 3D visualization +vtk = ">=9.5.2,<10" +pyvista = ">=0.46,<0.47" +trame = ">=3.8,<4" +trame-vuetify = ">=2.8,<3" +trame-vtk = ">=2.8,<3" + +# Jupyter notebooks (ipykernel and pixi-kernel are in base dependencies) +ipywidgets = ">=8.1,<9" +jupyterlab = ">=4.3,<5" + +# ============================================ +# DEV FEATURE (for development) +# ============================================ +# Code quality, documentation, AI assistance + +[feature.dev.dependencies] +# Code quality +black = ">=24,<25" +mypy = ">=1.8,<2" +ipdb = ">=0.13,<0.14" + +# Documentation +jupyter-ai = ">=2.31,<3" +jupytext = ">=1.16,<2" + +# Sphinx API documentation +sphinx = "*" +furo = "*" +myst-nb = "*" + +# Claude Code +nodejs = ">=18" + +[feature.dev.pypi-dependencies] +anthropic = "*" +sphinx-math-dollar = "*" + +[feature.dev.tasks] +install-claude = "npm install -g @anthropic-ai/claude-code" +claude = "claude" +docs-build = { cmd = "quarto render", cwd = "docs", env = { QUARTO_PYTHON = "python" } } +api-docs-build = { cmd = "python -m sphinx -b html docs/api docs/api/_build/html", env = { QUARTO_PYTHON = "python" } } +api-docs-clean = { cmd = "rm -rf docs/api/_build" } +docs-audit = { cmd = "python scripts/docs_audit.py", description = "Audit API documentation coverage" } + +# ============================================ +# ENVIRONMENT DEFINITIONS +# ============================================ + +[environments] +# --- Default Track (conda-forge PETSc) --- +# Minimal: Just build and run underworld3 +default = { features = ["conda-petsc"], solve-group = "default" } + +# Runtime: Run tutorials and examples (viz, jupyter) +runtime = { features = ["conda-petsc", "runtime"], solve-group = "default" } + +# Dev: Full development environment +dev = { features = ["conda-petsc", "runtime", "dev"], solve-group = "default" } + +# --- AMR Track (custom PETSc with mesh adaptation) --- +# AMR Minimal: Build custom PETSc, then underworld3 +amr = { features = ["amr"], solve-group = "amr" } + +# AMR Runtime: For tutorials with adaptive mesh +amr-runtime = { features = ["amr", "runtime"], solve-group = "amr" } + +# AMR Dev: Full development with custom PETSc +amr-dev = { features = ["amr", "runtime", "dev"], solve-group = "amr" } diff --git a/scripts/docs_audit.py b/scripts/docs_audit.py new file mode 100644 index 00000000..e3427d16 --- /dev/null +++ b/scripts/docs_audit.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python3 +""" +Audit underworld3 API documentation coverage. + +Compares what's exported in the package vs what's documented in docs/api/*.md +to identify gaps in documentation coverage. + +Usage: + pixi run docs-audit + python scripts/docs_audit.py +""" + +import inspect +import re +import sys +from pathlib import Path + + +def get_documented_items(docs_dir: Path) -> dict[str, set[str]]: + """Extract all documented items from .md files.""" + documented = {} + + for md_file in docs_dir.glob("*.md"): + if md_file.name == "index.md": + continue + + content = md_file.read_text() + module_name = md_file.stem + + # Find all autofunction, autoclass, automodule directives + patterns = [ + r'\.\. autofunction:: ([\w.]+)', + r'\.\. autoclass:: ([\w.]+)', + r'\.\. automodule:: ([\w.]+)', + ] + + items = set() + for pattern in patterns: + matches = re.findall(pattern, content) + items.update(matches) + + documented[module_name] = items + + return documented + + +def get_exported_items() -> dict[str, dict[str, list]]: + """Get all public items exported by underworld3.""" + import underworld3 as uw + + exports = {} + + # Key modules to audit + modules_to_check = [ + ('meshing', uw.meshing), + ('discretisation', uw.discretisation), + ('swarm', uw.swarm), + ('function', uw.function), + ('systems.solvers', uw.systems.solvers), + ('constitutive_models', uw.constitutive_models), + ('coordinates', uw.coordinates), + ('maths', uw.maths), + ('visualisation', uw.visualisation), + ('utilities', uw.utilities), + ('scaling', uw.scaling), + ('systems.ddt', uw.systems.ddt), + ('adaptivity', uw.adaptivity), + ('materials', uw.materials), + ('model', uw.model), + ] + + for mod_name, mod in modules_to_check: + classes = [] + functions = [] + + for name in dir(mod): + if name.startswith('_'): + continue + + obj = getattr(mod, name) + + # Check if it's defined in underworld3 + obj_module = getattr(obj, '__module__', '') + if not obj_module.startswith('underworld3'): + continue + + full_name = f"underworld3.{mod_name}.{name}" + + if inspect.isclass(obj): + classes.append((name, full_name)) + elif inspect.isfunction(obj): + functions.append((name, full_name)) + + exports[mod_name] = { + 'classes': classes, + 'functions': functions, + } + + return exports + + +def audit_documentation(): + """Run the documentation audit.""" + docs_dir = Path("docs/api") + + if not docs_dir.exists(): + print(f"ERROR: {docs_dir} not found. Run from repository root.") + sys.exit(1) + + print("=" * 70) + print("UNDERWORLD3 DOCUMENTATION AUDIT") + print("=" * 70) + + # Get what's documented + documented = get_documented_items(docs_dir) + + # Get what's exported + try: + exports = get_exported_items() + except ImportError as e: + print(f"ERROR: Could not import underworld3: {e}") + print("Make sure underworld3 is installed (pixi run build)") + sys.exit(1) + + # Compare + total_documented = 0 + total_missing = 0 + missing_items = [] + + for mod_name, items in exports.items(): + # Find corresponding doc file + doc_key = mod_name.replace('.', '_').replace('systems_', '') + if doc_key == 'solvers': + doc_key = 'solvers' + elif doc_key == 'ddt': + doc_key = 'systems_ddt' + + doc_items = set() + for key, doc_set in documented.items(): + doc_items.update(doc_set) + + mod_missing = [] + mod_documented = 0 + + for name, full_name in items['classes'] + items['functions']: + # Check various possible documentation paths + possible_names = [ + full_name, + full_name.replace('systems.', ''), + f"underworld3.{name}", + ] + + if any(pn in doc_items for pn in possible_names): + mod_documented += 1 + else: + mod_missing.append((name, full_name, 'class' if (name, full_name) in items['classes'] else 'function')) + + total_documented += mod_documented + total_missing += len(mod_missing) + + if mod_missing: + missing_items.append((mod_name, mod_missing)) + + # Print results + print(f"\nSummary: {total_documented} documented, {total_missing} missing") + print("=" * 70) + + if missing_items: + print("\nMISSING DOCUMENTATION:") + print("-" * 70) + + for mod_name, items in missing_items: + print(f"\n{mod_name}:") + for name, full_name, item_type in items: + print(f" [{item_type:8}] {name}") + print(f" -> .. auto{item_type}:: {full_name}") + else: + print("\n✓ All public items are documented!") + + # Print doc file coverage + print("\n" + "=" * 70) + print("DOCUMENTATION FILES:") + print("-" * 70) + + for md_file in sorted(docs_dir.glob("*.md")): + if md_file.name == "index.md": + continue + item_count = len(documented.get(md_file.stem, set())) + print(f" {md_file.name}: {item_count} items") + + print("\n" + "=" * 70) + + # Return exit code based on missing items + return 1 if total_missing > 0 else 0 + + +if __name__ == "__main__": + sys.exit(audit_documentation()) From 5444f554376587f48e7d989c2ecfd615347cd8b3 Mon Sep 17 00:00:00 2001 From: Louis Moresi Date: Sat, 14 Mar 2026 08:45:04 +1100 Subject: [PATCH 13/21] Update README: binder badges for v0.99/v3.0.0/dev, fix CI badge filenames - Binder section now shows three versioned launch badges - Status badges updated from .yml to .yaml (workflow was renamed) - Added pointer to binder_wizard.py for custom repositories Underworld development team with AI support from Claude Code --- README.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 69be47ac..bf7be33f 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,12 @@ All `Underworld3` source code is released under the LGPL-3 open source licence. main branch -[![test_uw3](https://github.com/underworldcode/underworld3/actions/workflows/build_uw3_and_test.yml/badge.svg?branch=main)](https://github.com/underworldcode/underworld3/actions/workflows/build_uw3_and_test.yml) +[![test_uw3](https://github.com/underworldcode/underworld3/actions/workflows/build_uw3_and_test.yaml/badge.svg?branch=main)](https://github.com/underworldcode/underworld3/actions/workflows/build_uw3_and_test.yaml) development branch -[![test_uw3](https://github.com/underworldcode/underworld3/actions/workflows/build_uw3_and_test.yml/badge.svg?branch=development)](https://github.com/underworldcode/underworld3/actions/workflows/build_uw3_and_test.yml) +[![test_uw3](https://github.com/underworldcode/underworld3/actions/workflows/build_uw3_and_test.yaml/badge.svg?branch=development)](https://github.com/underworldcode/underworld3/actions/workflows/build_uw3_and_test.yaml) ## Documentation @@ -29,12 +29,17 @@ The full documentation including tutorials, API reference, and developer guides ## Binder demonstration version - - [Launch on Binder](https://mybinder.org/v2/gh/underworldcode/uw3-binder-launcher/development?labpath=underworld3%2Fdocs%2Fbeginner%2Ftutorials%2FNotebook_Index.ipynb) +Try Underworld3 in your browser — no installation required: + - [![Launch v0.99 on Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/underworldcode/uw3-binder-launcher/v0.99?urlpath=git-pull%3Frepo%3Dhttps%25253A%25252F%25252Fgithub.com%25252Funderworldcode%25252Funderworld3%26branch%3Dmain%26urlpath%3Dlab%25252Ftree%25252Funderworld3) **v0.99** — JOSS publication release (stable) + - [![Launch v3.0.0 on Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/underworldcode/uw3-binder-launcher/v3.0.0?urlpath=git-pull%3Frepo%3Dhttps%25253A%25252F%25252Fgithub.com%25252Funderworldcode%25252Funderworld3%26branch%3Dmain%26urlpath%3Dlab%25252Ftree%25252Funderworld3) **v3.0.0** — current release + - [![Launch development on Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/underworldcode/uw3-binder-launcher/development?urlpath=git-pull%3Frepo%3Dhttps%25253A%25252F%25252Fgithub.com%25252Funderworldcode%25252Funderworld3%26branch%3Ddevelopment%26urlpath%3Dlab%25252Ftree%25252Funderworld3) **development** — bleeding edge + +Use `scripts/binder_wizard.py` to generate launch URLs for your own repositories using the Underworld3 environment. ## Installation Guide -The quickest option is **not to install** anything but try the binder demo above! +The quickest option is **not to install** anything but try the binder demos above! ### Quick Install (recommended) From f344bf53956b637fd19f243e0b49b60c5b1d0e0f Mon Sep 17 00:00:00 2001 From: Louis Moresi Date: Sat, 14 Mar 2026 11:07:42 +1100 Subject: [PATCH 14/21] Fix launcher dispatch payload to match expected field names Launcher expects {branch, ref_type} but we were sending {ref, is_release}. This prevented tag builds from creating launcher branches. Underworld development team with AI support from Claude Code --- .github/workflows/binder-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/binder-image.yml b/.github/workflows/binder-image.yml index 2feb4fcd..d3ddd768 100644 --- a/.github/workflows/binder-image.yml +++ b/.github/workflows/binder-image.yml @@ -87,4 +87,4 @@ jobs: token: ${{ secrets.LAUNCHER_PAT }} repository: underworldcode/uw3-binder-launcher event-type: image-updated - client-payload: '{"ref": "${{ env.DOCKER_TAG }}", "is_release": "${{ env.IS_RELEASE }}"}' + client-payload: '{"branch": "${{ env.DOCKER_TAG }}", "ref_type": "${{ env.IS_RELEASE == 'true' && 'tag' || 'branch' }}"}' From 0d62fa9caeab6b4998e740d513db2d22e6a2a426 Mon Sep 17 00:00:00 2001 From: Louis Moresi Date: Sat, 14 Mar 2026 12:28:53 +1100 Subject: [PATCH 15/21] Fix YAML quoting in launcher dispatch payload Compute REF_TYPE in shell instead of inline GitHub expression to avoid single-quote conflicts in YAML string. Previous version had nested single quotes that broke the payload. Underworld development team with AI support from Claude Code --- .github/workflows/binder-image.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/binder-image.yml b/.github/workflows/binder-image.yml index d3ddd768..9990fa16 100644 --- a/.github/workflows/binder-image.yml +++ b/.github/workflows/binder-image.yml @@ -52,15 +52,18 @@ jobs: if [[ "$GITHUB_REF" == refs/tags/* ]]; then REF_NAME=${GITHUB_REF#refs/tags/} IS_RELEASE=true + REF_TYPE=tag else REF_NAME=${GITHUB_REF#refs/heads/} IS_RELEASE=false + REF_TYPE=branch fi # Docker tags cannot contain slashes — sanitize for image tagging DOCKER_TAG=$(echo "$REF_NAME" | tr '/' '-') echo "REF_NAME=${REF_NAME}" >> $GITHUB_ENV echo "DOCKER_TAG=${DOCKER_TAG}" >> $GITHUB_ENV echo "IS_RELEASE=${IS_RELEASE}" >> $GITHUB_ENV + echo "REF_TYPE=${REF_TYPE}" >> $GITHUB_ENV - name: Build and push Docker image uses: docker/build-push-action@v5 @@ -87,4 +90,4 @@ jobs: token: ${{ secrets.LAUNCHER_PAT }} repository: underworldcode/uw3-binder-launcher event-type: image-updated - client-payload: '{"branch": "${{ env.DOCKER_TAG }}", "ref_type": "${{ env.IS_RELEASE == 'true' && 'tag' || 'branch' }}"}' + client-payload: '{"branch": "${{ env.DOCKER_TAG }}", "ref_type": "${{ env.REF_TYPE }}"}' From ecf30ae06a5053a25291811a66192880e8d7f4f4 Mon Sep 17 00:00:00 2001 From: Louis Moresi Date: Sat, 14 Mar 2026 15:04:01 +1100 Subject: [PATCH 16/21] Add manual dispatch overrides for binder image builds New workflow_dispatch inputs: - uw3_branch: override which branch/tag to clone (e.g. v0.99) - image_tag: override Docker image tag (e.g. v0.99) Enables building images for old tags that predate the workflow. Example: run from development with uw3_branch=v0.99, image_tag=v0.99 to build uw3-base:v0.99-slim using the current Dockerfile. Underworld development team with AI support from Claude Code --- .github/workflows/binder-image.yml | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/.github/workflows/binder-image.yml b/.github/workflows/binder-image.yml index 9990fa16..b2c04a1b 100644 --- a/.github/workflows/binder-image.yml +++ b/.github/workflows/binder-image.yml @@ -21,6 +21,14 @@ on: - 'setup.py' workflow_dispatch: inputs: + uw3_branch: + description: 'UW3 branch/tag to clone and build (e.g. v0.99, development). Leave empty to use the triggering branch.' + type: string + default: '' + image_tag: + description: 'Docker image tag override (e.g. v0.99). Leave empty to derive from branch. Sets IS_RELEASE=true if starts with v.' + type: string + default: '' force_rebuild: description: 'Force full rebuild (no cache)' type: boolean @@ -58,8 +66,23 @@ jobs: IS_RELEASE=false REF_TYPE=branch fi - # Docker tags cannot contain slashes — sanitize for image tagging - DOCKER_TAG=$(echo "$REF_NAME" | tr '/' '-') + + # Apply manual dispatch overrides + if [[ -n "${{ inputs.uw3_branch }}" ]]; then + REF_NAME="${{ inputs.uw3_branch }}" + fi + if [[ -n "${{ inputs.image_tag }}" ]]; then + DOCKER_TAG="${{ inputs.image_tag }}" + # Tags starting with v are releases + if [[ "$DOCKER_TAG" == v* ]]; then + IS_RELEASE=true + REF_TYPE=tag + fi + else + # Docker tags cannot contain slashes — sanitize for image tagging + DOCKER_TAG=$(echo "$REF_NAME" | tr '/' '-') + fi + echo "REF_NAME=${REF_NAME}" >> $GITHUB_ENV echo "DOCKER_TAG=${DOCKER_TAG}" >> $GITHUB_ENV echo "IS_RELEASE=${IS_RELEASE}" >> $GITHUB_ENV From dce52b4b51a3699c079c01c104db0c532734e561 Mon Sep 17 00:00:00 2001 From: Louis Moresi Date: Sat, 14 Mar 2026 17:09:41 +1100 Subject: [PATCH 17/21] Make Dockerfile resilient to different dependency versions Move version-specific lib subdirectories (vtk-X.Y, openvino-X.Y.Z) into a generic split step using wildcards instead of hardcoded paths. This allows the same Dockerfile to build images for different UW3 versions that may have different package versions in their pixi env. Underworld development team with AI support from Claude Code --- container/Dockerfile.base.optimized | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/container/Dockerfile.base.optimized b/container/Dockerfile.base.optimized index 4907eb42..5c9dd93e 100644 --- a/container/Dockerfile.base.optimized +++ b/container/Dockerfile.base.optimized @@ -98,6 +98,7 @@ RUN rm -rf /home/jovyan/underworld3/.pixi/envs/runtime/conda-meta RUN mkdir -p /home/jovyan/lib-split/llvm && \ mkdir -p /home/jovyan/lib-split/vtk && \ mkdir -p /home/jovyan/lib-split/other-large && \ + mkdir -p /home/jovyan/lib-split/versioned && \ mkdir -p /home/jovyan/lib-split/remaining # Move LLVM libraries (~355MB) @@ -117,6 +118,11 @@ RUN cd /home/jovyan/underworld3/.pixi/envs/runtime/lib && \ mv libQt*.so* /home/jovyan/lib-split/other-large/ 2>/dev/null || true && \ mv libicu*.so* /home/jovyan/lib-split/other-large/ 2>/dev/null || true +# Move versioned subdirectories (vtk-X.Y, openvino-X.Y.Z) — names vary by env +RUN cd /home/jovyan/underworld3/.pixi/envs/runtime/lib && \ + mv vtk-* /home/jovyan/lib-split/versioned/ 2>/dev/null || true && \ + mv openvino-* /home/jovyan/lib-split/versioned/ 2>/dev/null || true + # ============================================================================= # Final stage # ============================================================================= @@ -182,9 +188,8 @@ COPY --from=builder --chown=jovyan:jovyan /home/jovyan/underworld3/.pixi/envs/ru COPY --from=builder --chown=jovyan:jovyan /home/jovyan/underworld3/.pixi/envs/runtime/lib/qt6 /home/jovyan/underworld3/.pixi/envs/runtime/lib/qt6 COPY --from=builder --chown=jovyan:jovyan /home/jovyan/underworld3/.pixi/envs/runtime/lib/dri /home/jovyan/underworld3/.pixi/envs/runtime/lib/dri -# Layer 5b: VTK and openvino directories (~140MB) -COPY --from=builder --chown=jovyan:jovyan /home/jovyan/underworld3/.pixi/envs/runtime/lib/vtk-9.5 /home/jovyan/underworld3/.pixi/envs/runtime/lib/vtk-9.5 -COPY --from=builder --chown=jovyan:jovyan /home/jovyan/underworld3/.pixi/envs/runtime/lib/openvino-2025.2.0 /home/jovyan/underworld3/.pixi/envs/runtime/lib/openvino-2025.2.0 +# Layer 5b: Versioned subdirectories (vtk-X.Y, openvino-X.Y.Z — names vary by env) +COPY --from=builder --chown=jovyan:jovyan /home/jovyan/lib-split/versioned /home/jovyan/underworld3/.pixi/envs/runtime/lib/ # Layer 5c: LLVM/Clang libraries moved to split dir (~350MB) COPY --from=builder --chown=jovyan:jovyan /home/jovyan/lib-split/llvm /home/jovyan/underworld3/.pixi/envs/runtime/lib/ From 9aeaadd2f32208d20a65d3a5bf9c606f0353875c Mon Sep 17 00:00:00 2001 From: Louis Moresi Date: Sun, 15 Mar 2026 22:47:15 +1100 Subject: [PATCH 18/21] Fix stale build cache: add --no-cache-dir to pip install MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UW3 is always version 0.0.0, so pip's wheel cache treats every build as "already cached" and silently reuses stale code. This was the most common build issue — source changes weren't reflected after ./uw build. Underworld development team with AI support from Claude Code --- uw | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/uw b/uw index 6fed7a5f..9a8158e7 100755 --- a/uw +++ b/uw @@ -165,7 +165,10 @@ run_build() { echo "$current_target" > "$petsc_marker" echo " Building underworld3..." - $PIXI run -e "$env" pip install . --no-build-isolation || { + # --no-cache-dir: UW3 is always version 0.0.0, so pip's wheel cache + # treats every build as "already cached" and silently reuses stale code. + # This flag forces pip to rebuild from source every time. + $PIXI run -e "$env" pip install . --no-build-isolation --no-cache-dir || { echo -e "${YELLOW}underworld3 build failed${NC}" exit 1 } From 4117a72fd0aefa952c44c3f728a3060f7b77801f Mon Sep 17 00:00:00 2001 From: Louis Moresi Date: Sat, 14 Mar 2026 15:19:24 +1100 Subject: [PATCH 19/21] Fix petsc_save_checkpoint() to use XDMF compat groups (fixes #80) petsc_save_checkpoint() now delegates to write_timestep(), gaining vertex/cell compatibility groups, field projection, and tensor repacking for ParaView. Previously it used the legacy generateXdmf() which missed all recent XDMF improvements. The outputPath argument is split into directory + filename to map correctly onto write_timestep()'s (filename, outputPath) interface. Underworld development team with AI support from Claude Code --- .../discretisation/discretisation_mesh.py | 68 +++++++++++-------- 1 file changed, 40 insertions(+), 28 deletions(-) diff --git a/src/underworld3/discretisation/discretisation_mesh.py b/src/underworld3/discretisation/discretisation_mesh.py index 925bdab9..cb339c63 100644 --- a/src/underworld3/discretisation/discretisation_mesh.py +++ b/src/underworld3/discretisation/discretisation_mesh.py @@ -1820,46 +1820,58 @@ def petsc_save_checkpoint( meshVars: Optional[list] = [], outputPath: Optional[str] = "", ): - """ + """Save the mesh and mesh variables to HDF5 with XDMF. - Use PETSc to save the mesh and mesh vars in a h5 and xdmf file. + This is a convenience wrapper around ``write_timestep()`` that + provides the simpler interface used by earlier Underworld3 code. + Output uses the same per-variable file layout and XDMF generation + (including vertex/cell compatibility groups, field projection, and + tensor repacking) as ``write_timestep()``. Parameters ---------- - meshVars: - List of UW mesh variables to save. If left empty then just the mesh is saved. + meshVars : + List of UW mesh variables to save. If left empty then just + the mesh is saved. index : - An index which might correspond to the timestep or output number (for example). + An index which might correspond to the timestep or output + number (for example). outputPath : - Path to save the data. If left empty it will save the data in the current working directory. + Path to save the data. If left empty it will save the data + in the current working directory. """ - if meshVars != None and not isinstance(meshVars, list): + if meshVars is not None and not isinstance(meshVars, list): raise RuntimeError("`meshVars` does not appear to be a list.") - from underworld3.utilities import generateXdmf - - ### save mesh vars - fname = f"./{outputPath}{'_step_'}{index:05d}.h5" - xfname = f"./{outputPath}{'_step_'}{index:05d}.xdmf" - #### create petsc viewer - viewer = PETSc.ViewerHDF5().createHDF5( - fname, mode=PETSc.Viewer.Mode.WRITE, comm=PETSc.COMM_WORLD + # Split outputPath into directory and filename base for write_timestep(). + # Old callers pass outputPath like './output/' or './output/run_name'. + import os + + outputPath = outputPath or "" + if outputPath.endswith(os.sep) or outputPath.endswith("/"): + # Directory only — use 'checkpoint' as the file base name + directory = outputPath + filename = "checkpoint" + elif os.sep in outputPath or "/" in outputPath: + # Path with filename component + directory = os.path.dirname(outputPath) + filename = os.path.basename(outputPath) + else: + # Bare name, no directory + directory = "" + filename = outputPath if outputPath else "checkpoint" + + self.write_timestep( + filename=filename, + index=index, + outputPath=directory, + meshVars=meshVars if meshVars is not None else [], + swarmVars=[], + meshUpdates=False, + create_xdmf=True, ) - viewer(self.dm) - - ### Empty meshVars will save just the mesh - if meshVars != None: - for var in meshVars: - var._sync_lvec_to_gvec() - viewer(var._gvec) - - viewer.destroy() - - if uw.mpi.rank == 0: - generateXdmf(fname, xfname) - @timing.routine_timer_decorator def write_checkpoint( self, From e3ef3b3fdc2a3464acca9029a6d7927034d51b2d Mon Sep 17 00:00:00 2001 From: Louis Moresi Date: Tue, 17 Mar 2026 17:23:11 +1100 Subject: [PATCH 20/21] Auto-detect binder launcher ref from git tag / RTD version The docs landing page binder link was hardcoded to 'development', so tagged RTD builds (e.g. v3.0.0) pointed to the wrong environment. Now conf.py detects the build context (git tag > READTHEDOCS_VERSION > branch > fallback) and injects the correct ref via MyST substitution. Also derives version/release from setuptools-scm instead of hardcoding. Underworld development team with AI support from Claude Code --- docs/conf.py | 71 +++++++++++++++++++++++++++++++++++++++++++++++++-- docs/index.md | 2 +- 2 files changed, 70 insertions(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 333c272c..5437b7b7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -2,14 +2,68 @@ # Unified build: User guides, tutorials, and API reference # NOTE: underworld3 is installed via 'pixi run build' before docs build +import os +import subprocess + +# ============================================================================= +# Version detection (from git tags via setuptools-scm) +# ============================================================================= + +def _get_version(): + """Get version from setuptools-scm, falling back gracefully.""" + try: + from setuptools_scm import get_version + return get_version(root=os.path.join(os.path.dirname(__file__), "..")) + except Exception: + return "0.0.0" + +def _get_binder_ref(): + """Determine binder launcher ref from build context. + + Priority: exact git tag > RTD version > git branch > fallback. + The returned value is used in binder URLs as the launcher repo ref. + """ + repo_root = os.path.join(os.path.dirname(__file__), "..") + + # 1. Exact git tag (release builds) + try: + tag = subprocess.check_output( + ["git", "describe", "--tags", "--exact-match"], + cwd=repo_root, text=True, stderr=subprocess.DEVNULL, + ).strip() + return tag # e.g. "v3.0.0" + except (subprocess.CalledProcessError, FileNotFoundError): + pass + + # 2. ReadTheDocs version (set automatically by RTD) + rtd_version = os.environ.get("READTHEDOCS_VERSION") + if rtd_version and rtd_version not in ("latest", "stable"): + return rtd_version + + # 3. Git branch name + try: + branch = subprocess.check_output( + ["git", "rev-parse", "--abbrev-ref", "HEAD"], + cwd=repo_root, text=True, stderr=subprocess.DEVNULL, + ).strip() + if branch == "main": + return "main" + except (subprocess.CalledProcessError, FileNotFoundError): + pass + + # 4. Fallback + return "development" + +_full_version = _get_version() + # ============================================================================= # Project Information # ============================================================================= project = 'Underworld3' copyright = '2025, Underworld Team' author = 'Underworld Team' -version = '0.9' -release = '0.9b' +version = _full_version.split('+')[0] # e.g. "3.0.1.dev5" +release = _full_version.split('+')[0] # ============================================================================= # Extensions @@ -37,11 +91,24 @@ "fieldlist", # Field lists "tasklist", # - [ ] task lists "attrs_inline", # {#id .class} + "substitution", # {{key}} substitutions in .md files ] # Allow dollar signs for math myst_dmath_double_inline = True +# Dynamic substitutions — available in all MyST markdown files as {{key}} +_binder_ref = _get_binder_ref() +myst_substitutions = { + "binder_ref": _binder_ref, + "binder_badge": ( + f"[![Binder](https://mybinder.org/badge_logo.svg)]" + f"(https://mybinder.org/v2/gh/underworldcode/uw3-binder-launcher/" + f"{_binder_ref}?labpath=underworld3%2Fdocs%2Fbeginner%2Ftutorials" + f"%2FNotebook_Index.ipynb)" + ), +} + # Source file types source_suffix = { '.rst': 'restructuredtext', diff --git a/docs/index.md b/docs/index.md index 595647be..0dcb49cf 100644 --- a/docs/index.md +++ b/docs/index.md @@ -162,7 +162,7 @@ Underworld3 is a Python library for computational geodynamics, built on: Launch interactive tutorials directly in your browser: -[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/underworldcode/uw3-binder-launcher/development?labpath=underworld3%2Fdocs%2Fbeginner%2Ftutorials%2FNotebook_Index.ipynb) +{{binder_badge}} ```{toctree} :maxdepth: 2 From 4526d1f1f04017049fd469ccfc919a527e0efc13 Mon Sep 17 00:00:00 2001 From: ss2098 Date: Mon, 8 Jun 2026 20:30:24 -0800 Subject: [PATCH 21/21] Fix units setup in vector projection Poisson test --- tests/test_1450_poisson_vector_projection.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/test_1450_poisson_vector_projection.py b/tests/test_1450_poisson_vector_projection.py index 65addf4b..3fdf39ee 100644 --- a/tests/test_1450_poisson_vector_projection.py +++ b/tests/test_1450_poisson_vector_projection.py @@ -64,6 +64,17 @@ def test_vector_projection_after_poisson(): def test_vector_projection_after_poisson_with_units(): + # Strict units mode requires reference quantities to be set before + # creating meshes or MeshVariables with units. + uw.reset_default_model() + model = uw.get_default_model() + model.set_reference_quantities( + domain_depth=uw.quantity(1.0, "m"), + plate_velocity=uw.quantity(1.0, "m/s"), + mantle_viscosity=uw.quantity(1.0, "Pa*s"), + temperature_difference=uw.quantity(1.0, "K"), + ) + """ Test that Vector_Projection works with unit-aware meshes after Poisson solve.