Skip to content

Add February 2026 Architectural Reviews - #45

Merged
lmoresi merged 3 commits into
developmentfrom
review/architecture-2026-02
Mar 10, 2026
Merged

Add February 2026 Architectural Reviews#45
lmoresi merged 3 commits into
developmentfrom
review/architecture-2026-02

Conversation

@lmoresi

@lmoresi lmoresi commented Feb 1, 2026

Copy link
Copy Markdown
Member

Summary

New Reviews

1. UNITS-SYSTEM-ARCHITECTURAL-REVIEW.md (UW3-2026-02-001)

Documents the current units system architecture:

  • Gateway Pattern: Units handled at boundaries (input/output), not during symbolic manipulation
  • Transparent Container Principle: UWexpression derives properties from contents, never caches separately
  • Codebase: ~6,155 LOC across 6 core modules
  • Consolidation: ~940 lines of deprecated mixin code removed in January 2026
  • Test Coverage: 60+ tests in 07xx/08xx ranges

2. DATA-ACCESS-MATHEMATICAL-INTERFACE-REVIEW.md (UW3-2026-02-002)

Documents the data access and mathematical interface:

  • NDArray_With_Callback: Automatic PETSc synchronization via callbacks
  • MathematicalMixin: Natural mathematical notation (velocity * density without .sym)
  • Delegation Pattern: EnhancedMeshVariable delegates to _BaseMeshVariable (fixed Jan 2026)
  • Codebase: ~6,183 LOC across 4 core components
  • Test Coverage: ~75 tests in 01xx/05xx/06xx ranges

Test plan

  • Review documents are accessible and render correctly
  • Links to related documentation work
  • Supersedes information is accurate

🤖 Generated with Claude Code

…tems

- UNITS-SYSTEM-ARCHITECTURAL-REVIEW.md: Documents the Gateway Pattern
  architecture, ~6,155 LOC across 6 core modules, and January 2026
  consolidation that removed ~940 lines of deprecated mixin code

- DATA-ACCESS-MATHEMATICAL-INTERFACE-REVIEW.md: Documents NDArray_With_Callback,
  MathematicalMixin, and EnhancedMeshVariable wrapper layer with delegation
  pattern fix that eliminated 425 lines of duplicate code

- Updated docs/reviews/README.md with February 2026 section

These reviews supersede the November 2025 reviews (UW3-2025-11-002 and
UW3-2025-11-003) which were based on a now-outdated codebase state.

Underworld development team with AI support from Claude Code
Copilot AI review requested due to automatic review settings February 1, 2026 03:45
@github-actions

github-actions Bot commented Feb 1, 2026

Copy link
Copy Markdown

🔍 Architectural Review Checklist

Thank you for submitting an architectural review! Reviewers should validate:

Design & Architecture

  • Design rationale is clear and well-justified
  • Trade-offs are documented with alternatives considered
  • System architecture is comprehensible
  • Integration points are clearly identified

Implementation

  • Implementation matches documented design
  • Code quality meets project standards
  • Breaking changes are identified and justified
  • Backward compatibility is properly addressed

Testing & Validation

  • Testing strategy is adequate for the changes
  • Test coverage is sufficient
  • Edge cases are properly covered
  • Performance impact has been assessed

Documentation

  • Known limitations are clearly documented
  • Benefits are quantified with metrics
  • User-facing changes are documented
  • Migration guide provided (if needed)

Review Process: See CODE-REVIEW-PROCESS.md

Approval: This PR merge = Review formally approved

@github-actions

github-actions Bot commented Feb 1, 2026

Copy link
Copy Markdown

Test Suite: success

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds February 2026 architectural review documentation for the Units System and the Data Access / Mathematical Interface, and updates the reviews index to reference these new documents and include them in the summary table.

Changes:

  • Added two new architectural review documents under docs/reviews/2026-02/.
  • Updated docs/reviews/README.md with a new “2026 Reviews” section and new rows in the Review Summary Table.
  • Updated the README “Last Updated” date.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 8 comments.

File Description
docs/reviews/README.md Adds Feb 2026 review entries and includes them in the summary table.
docs/reviews/2026-02/UNITS-SYSTEM-ARCHITECTURAL-REVIEW.md New Units System architectural review document (UW3-2026-02-001).
docs/reviews/2026-02/DATA-ACCESS-MATHEMATICAL-INTERFACE-REVIEW.md New Data Access / Mathematical Interface architectural review document (UW3-2026-02-002).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +19 to +24
| `scaling/_scaling.py` | ~656 | Model scaling infrastructure |
| `scaling/units.py` | ~1,993 | Pint registry, unit definitions |
| `function/quantities.py` | ~859 | UWQuantity class |
| `function/expressions.py` | ~1,797 | UWexpression (lazy evaluation) |
| `function/nondimensional.py` | ~350 | Non-dimensionalization utilities |
| `function/unit_conversion.py` | ~500 | Conversion utilities, get_units() |

Copilot AI Feb 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Several module paths in this metrics table don't exist in the repository (e.g., src/underworld3/scaling/units.py and src/underworld3/function/nondimensional.py). Consider updating these entries to the actual locations (notably src/underworld3/units.py and src/underworld3/utilities/nondimensional.py / src/underworld3/scaling/_scaling.py) so readers can navigate to the code.

Suggested change
| `scaling/_scaling.py` | ~656 | Model scaling infrastructure |
| `scaling/units.py` | ~1,993 | Pint registry, unit definitions |
| `function/quantities.py` | ~859 | UWQuantity class |
| `function/expressions.py` | ~1,797 | UWexpression (lazy evaluation) |
| `function/nondimensional.py` | ~350 | Non-dimensionalization utilities |
| `function/unit_conversion.py` | ~500 | Conversion utilities, get_units() |
| `src/underworld3/scaling/_scaling.py` | ~656 | Model scaling infrastructure |
| `src/underworld3/units.py` | ~1,993 | Pint registry, unit definitions |
| `src/underworld3/function/quantities.py` | ~859 | UWQuantity class |
| `src/underworld3/function/expressions.py` | ~1,797 | UWexpression (lazy evaluation) |
| `src/underworld3/utilities/nondimensional.py` | ~350 | Non-dimensionalization utilities |
| `src/underworld3/function/unit_conversion.py` | ~500 | Conversion utilities, get_units() |

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — the metrics table now uses the correct paths: units.py, utilities/nondimensional.py, etc. Verified against the current source tree.

Comment on lines +98 to +104
class UWexpression:
@property
def units(self):
# Always derived, never stored separately
if self._value_with_units is not None:
return self._value_with_units.units # From contained atom
return get_units(self._sym) # From contained tree

Copilot AI Feb 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The example UWexpression.units implementation here refers to _value_with_units and get_units(self._sym), but the current implementation in src/underworld3/function/expressions.py derives units via self._sym.units (and _value_with_units is not an attribute on UWexpression). Please update this snippet to match the actual code to avoid misleading readers about where unit metadata lives.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — the code snippet now matches the actual implementation in expressions.py:881: derives units via self._sym.units.

Comment on lines +227 to +232
| `src/underworld3/scaling/_scaling.py` | Model scaling infrastructure |
| `src/underworld3/scaling/units.py` | Pint registry configuration |
| `src/underworld3/function/quantities.py` | UWQuantity implementation |
| `src/underworld3/function/expressions.py` | UWexpression implementation |
| `src/underworld3/function/nondimensional.py` | Non-dimensionalization |
| `src/underworld3/function/unit_conversion.py` | get_units(), has_units() |

Copilot AI Feb 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "Key Files" table lists src/underworld3/scaling/units.py and src/underworld3/function/nondimensional.py, which are not present, and it attributes get_units()/has_units() to function/unit_conversion.py (those entry points are in src/underworld3/units.py). Please correct these references so the document points to the real source files.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — Key Files table corrected to match actual source paths.

Comment on lines +42 to +57
EnhancedMeshVariable (persistence.py) ← THIS IS WHAT USERS GET
- Wraps _BaseMeshVariable
- Adds: Math operations, units support, persistence
- DELEGATES .array property to base
_BaseMeshVariable (discretisation_mesh_variables.py)
- Low-level PETSc interface
- Owns array view classes (SimpleMeshArrayView, TensorMeshArrayView)
- Direct PETSc vector management
```

**Key Discovery**: `MeshVariable` is an **alias** for `EnhancedMeshVariable`:
```python
# src/underworld3/discretisation/__init__.py line 2:
from .persistence import EnhancedMeshVariable as MeshVariable
```

Copilot AI Feb 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The architecture diagram + alias snippet points to EnhancedMeshVariable (persistence.py) and shows from .persistence import EnhancedMeshVariable as MeshVariable, but src/underworld3/discretisation/__init__.py currently imports EnhancedMeshVariable from .enhanced_variables (line 28). Please update the diagram and code snippet to reflect the actual module so readers can find the implementation.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — the diagram and alias snippet now correctly reference enhanced_variables.py:

from .enhanced_variables import EnhancedMeshVariable as MeshVariable


## Core Components

### 1. NDArray_With_Callback (`utilities/nd_array_with_callback.py`)

Copilot AI Feb 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section references utilities/nd_array_with_callback.py, but the NDArray_With_Callback implementation is in src/underworld3/utilities/nd_array_callback.py. Updating the file name here (and anywhere else it’s cited) will keep the review navigable.

Suggested change
### 1. NDArray_With_Callback (`utilities/nd_array_with_callback.py`)
### 1. NDArray_With_Callback (`src/underworld3/utilities/nd_array_callback.py`)

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — all references now use nd_array_callback.py (the actual filename).

Comment on lines +166 to +175
### 3. EnhancedMeshVariable (`discretisation/persistence.py`)

User-facing wrapper combining multiple capabilities:

```python
class EnhancedMeshVariable(UnitAwareMixin, MathematicalMixin):
"""
Enhanced MeshVariable with:
- Mathematical operations (via MathematicalMixin)
- Units support (via UnitAwareMixin)

Copilot AI Feb 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The EnhancedMeshVariable example class signature uses UnitAwareMixin, but the current implementation is class EnhancedMeshVariable(DimensionalityMixin, MathematicalMixin) in src/underworld3/discretisation/enhanced_variables.py. Please align the snippet and surrounding text with the real mixin names to avoid confusion.

Suggested change
### 3. EnhancedMeshVariable (`discretisation/persistence.py`)
User-facing wrapper combining multiple capabilities:
```python
class EnhancedMeshVariable(UnitAwareMixin, MathematicalMixin):
"""
Enhanced MeshVariable with:
- Mathematical operations (via MathematicalMixin)
- Units support (via UnitAwareMixin)
### 3. EnhancedMeshVariable (`discretisation/enhanced_variables.py`)
User-facing wrapper combining multiple capabilities:
```python
class EnhancedMeshVariable(DimensionalityMixin, MathematicalMixin):
"""
Enhanced MeshVariable with:
- Mathematical operations (via MathematicalMixin)
- Dimensionality & units support (via DimensionalityMixin)

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — class signature now shows the actual mixins: class EnhancedMeshVariable(DimensionalityMixin, MathematicalMixin).

Comment on lines +370 to +372
| `src/underworld3/utilities/nd_array_with_callback.py` | Callback array implementation |
| `src/underworld3/utilities/mathematical_mixin.py` | Mathematical notation support |
| `src/underworld3/discretisation/persistence.py` | EnhancedMeshVariable |

Copilot AI Feb 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "Key Files" table lists paths that don't exist in this repo (e.g. src/underworld3/utilities/nd_array_with_callback.py and src/underworld3/discretisation/persistence.py as the EnhancedMeshVariable implementation). Please update these to the actual files (utilities/nd_array_callback.py and discretisation/enhanced_variables.py) so links and references remain accurate.

Suggested change
| `src/underworld3/utilities/nd_array_with_callback.py` | Callback array implementation |
| `src/underworld3/utilities/mathematical_mixin.py` | Mathematical notation support |
| `src/underworld3/discretisation/persistence.py` | EnhancedMeshVariable |
| `src/underworld3/utilities/nd_array_callback.py` | Callback array implementation |
| `src/underworld3/utilities/mathematical_mixin.py` | Mathematical notation support |
| `src/underworld3/discretisation/enhanced_variables.py` | EnhancedMeshVariable |

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — Key Files table corrected to nd_array_callback.py and enhanced_variables.py.

Comment thread docs/reviews/README.md
Comment on lines +263 to +266
| Review | Component | Tests | Pass Rate | Date | Status |
|--------|-----------|-------|-----------|------|--------|
| Units System (2026-02) | Units/Scaling | 60+ | 🔍 TBD | 2026-02-01 | 🔍 Under Review |
| Data Access (2026-02) | Array/Math Interface | 75+ | 🔍 TBD | 2026-02-01 | 🔍 Under Review |

Copilot AI Feb 1, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The archive now includes two February 2026 reviews, but the later "Statistics" section still only lists year 2025 (and omits 2026 components). Please update the "By Year" / "By Component" tables so they remain consistent with the newly added 2026 entries above.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — the Statistics section now includes 2026 entries in both "By Year" and "By Component" tables.

Add required sections to both architectural reviews:
- ## Changes Made section after Overview
- ## Testing Instructions (renamed from Testing Status)
- ## Known Limitations as top-level section

Restructure subsections to maintain proper hierarchy.

Underworld development team with AI support from Claude Code
@github-actions

github-actions Bot commented Feb 1, 2026

Copy link
Copy Markdown

Test Suite: success

Address Copilot review suggestions on PR #45:
- units.py is at src/underworld3/units.py (not scaling/units.py)
- nondimensional.py is in utilities/ (not function/)
- nd_array_callback.py (not nd_array_with_callback.py)
- EnhancedMeshVariable is in enhanced_variables.py (not persistence.py)
- EnhancedMeshVariable uses DimensionalityMixin (not UnitAwareMixin)
- UWexpression.units implementation uses hasattr(self._sym, 'units')
- Update README statistics to include 2026 reviews

Underworld development team with AI support from Claude Code
@github-actions

Copy link
Copy Markdown

Test Suite: success

@lmoresi

lmoresi commented Feb 24, 2026

Copy link
Copy Markdown
Member Author

Just been through to fix up the inconsistencies that copilot found. The review is our formal oversight of meeting a project milestone @jcgraciosa, @bknight1 - happy to talk you through the changes.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments and clarifications:

  1. Maybe this will be for future work, but when saving data, will these be in terms of dimensional values as well?

  2. To clarify, in indexing a tensor field, is it (N, row, col)?

  3. For the data access patterns, is it recommended to do batch synchronization if I'm updating more than 1 variable?

  4. Is the figure for the Callback Overhead (5% - 10%) also the same for parallel runs?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the thorough review. Responses to each question:

1. Saving dimensional data: Currently, HDF5 save/load writes non-dimensional values (what PETSc sees). Adding unit metadata to HDF5 output is on the medium-term roadmap — the plan is to store units as HDF5 attributes alongside the data arrays, so saved files can be re-loaded with full dimensional context. For now, users need to track their scaling model to re-dimensionalise on load.

2. Tensor indexing: Yes, exactly — (N, row, col). For a 3D stress tensor, stress.array[:, i, j] gives the (i,j) component across all N nodes. The convention matches standard matrix notation.

3. Batch synchronization: Yes, uw.synchronised_array_update() is recommended when updating multiple variables. Each individual write triggers a PETSc sync + MPI barriers, so batching avoids redundant synchronization. For a single variable, direct access is fine.

4. Callback overhead in parallel: The 5-10% figure is for serial. In parallel, the overhead is dominated by MPI barriers (entry, pre-callback, exit) rather than the callback mechanism itself. For large arrays the barrier cost is relatively small; for small arrays or frequent updates, batching becomes more important. We haven't profiled this systematically across different process counts yet — that's noted as a medium-term task.

Underworld development team with AI support from Claude Code

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few questions and clarifications:

  1. Do the MeshVariable data access patterns also follow the template? I.e.,
var.array[:, 0, 0] = scalar_values      # should these be dimensional values?
var.data[...] = values                          # should these be non-dimensional values? 
  1. Is there an example for accessing the units of MeshVar.sym * MeshVar.sym?

  2. For complex expressions, is there a recommended maximum depth for nested expressions?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the questions. Responses:

1. .array vs .data dimensional convention: Yes, that's the intended pattern:

  • var.array returns values in the user's dimensional units (what you'd measure physically)
  • var.data returns non-dimensional values (what PETSc works with internally)

So var.array[:, 0, 0] = temperature_in_kelvin and var.data[...] = nd_values is correct. The non-dimensionalisation happens automatically based on the active scaling model.

2. Units of MeshVar.sym * MeshVar.sym: The result is a SymPy expression, and units are discoverable via get_units():

T = uw.discretisation.MeshVariable("T", mesh, 1, units="K")
v = uw.discretisation.MeshVariable("v", mesh, mesh.dim, units="m/s")

# Symbolic product
expr = T.sym * v.sym[0]

# Discover units from the expression tree
from underworld3.function.unit_conversion import get_units
units = get_units(expr)  # walks the SymPy tree, finds units on atoms

The get_units() function traverses the expression tree, finds UnderworldFunction atoms, looks up their units, and combines them according to the arithmetic operations in the tree.

3. Maximum depth for nested expressions: There is no hard limit, but practically, deeply nested expressions (>10 levels of composition) can make get_units() traversal slower and unit propagation less reliable through certain SymPy transformations (e.g., simplify(), expand()). For typical geodynamics expressions (Rayleigh number, viscosity laws, yield criteria), nesting depth is 3-5 levels and works reliably. If you hit issues with very complex expressions, breaking them into named intermediate uw.expression() objects is the recommended approach — it also improves readability.

Underworld development team with AI support from Claude Code

@lmoresi
lmoresi merged commit 09b7907 into development Mar 10, 2026
3 of 4 checks passed
lmoresi added a commit that referenced this pull request Mar 10, 2026
Update sign-off tables, review comments, and README status for both
the Units System and Data Access reviews following PR #45 merge.
Copilot path corrections and jcgraciosa's clarification questions
are documented in each review's Resolution section.

Underworld development team with AI support from Claude Code
@lmoresi
lmoresi deleted the review/architecture-2026-02 branch June 13, 2026 00:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants