Skip to content

feature/SOF-7990 fix: PointsGridDataProvider derives gridMetricValue - #162

Open
VsevolodX wants to merge 6 commits into
mainfrom
feature/SOF-7990
Open

feature/SOF-7990 fix: PointsGridDataProvider derives gridMetricValue #162
VsevolodX wants to merge 6 commits into
mainfrom
feature/SOF-7990

Conversation

@VsevolodX

@VsevolodX VsevolodX commented Aug 8, 2026

Copy link
Copy Markdown
Member

Problem

Clone a job whose k-grid was set explicitly in a notebook, then try to change the k-grid on the clone: nothing happens. The value is stuck, the metric reads -1, and no error is shown.

PointsGridDataProvider(dimensions=SCF_KGRID, isEdited=True) — the exact call total_energy.ipynb makes — never supplies gridMetricValue. It defaults to the DEFAULT_KPPRA sentinel (-1) and gets persisted as if it were a deliberate, edited value:

>>> PointsGridDataProvider(dimensions=[4,4,4], isEdited=True).get_context_item_data()
{'name': 'kgrid', 'isEdited': True, 'data': {..., 'gridMetricValue': -1}, 'extraData': {}}

That is wrong regardless of downstream effect: a sentinel meaning "nothing was ever computed" gets marked isEdited: True and persisted as if it were the user's deliberate choice, and nothing downstream ever recomputes it (checked both sides — Python's _get_effective_data and JS's this.data?.gridMetricValue || this.defaultMetric.value, which treats -1 as truthy and passes it straight through).

Correction, added after initially opening this PR: I first claimed the observed "locked, can't edit" symptom was caused by the k-grid form's schema requiring gridMetricValue >= 1 for KPPRA, with RJSF re-validating the full form state on every edit and silently rejecting it. That claim was wrong. I verified it against a hand-copied version of the schema, not the actual compiled one; running it against the real KGridFormDataManager class shows the minimum: 1 constraint never actually reaches the schema RJSF validates against — getPatchedSchemaById's dot-notation merge (applyPatchWithDotNotation in @mat3ra/esse) can't create the missing intermediate dependencies key that the base ESSE schema doesn't have, so the whole patch silently no-ops. Confirmed: gridMetricValue: -1 validates true against the real schema, same as 64. So this PR does not have proof of why editing locks in the UI — only that the persisted -1 is wrong on its own terms. The dependencies patch being dead code is a separate, likely-unrelated bug worth its own look.

Fix

PointsGridDataProvider now derives gridMetricValue from dimensions (× an optional n_atoms, default 1) whenever dimensions are given explicitly but the metric is not — mirroring PointsGridFormDataProvider.setData() in JS, which already does exactly this when a user sets dimensions manually in the UI. An explicitly passed gridMetricValue is still respected; the untouched default path (no dimensions, no metric at all) is unaffected and correctly keeps the -1 sentinel, since there's nothing to derive a value from.

>>> PointsGridDataProvider(dimensions=[4,4,4], isEdited=True).get_context_item_data()
{'data': {..., 'gridMetricValue': 64}, ...}   # was -1

Test plan

  • pytest — 85 passed (baseline 81 + 4 new)
  • New tests cover: derives from dimensions (64 for [4,4,4], n_atoms=1); respects an explicit override; derives correctly with a real n_atoms; untouched default path keeps the sentinel
  • convergence_mixin.py's separate call pattern (PointsGridDataProvider(data=kgrid_data)) verified unaffected — it never sets the dimensions field, so the new derivation logic doesn't fire
  • Ruff clean (pre-existing files were already clean; confirmed via git stash)
  • CI green: https://github.com/mat3ra/wode/actions/runs/31282112756

Not fixed here

  • Numeric parity with JS's nAtoms requires the caller to actually pass a real atom count — n_atoms defaults to 1 since this provider has no material context of its own. Left as a follow-up in api-examples.
  • Whatever actually causes the reported UI lock is still unidentified. The -1 persistence is fixed regardless because it's wrong on its own terms, but I have not located why editing a cloned job's k-grid was observed to silently fail. That likely requires checking @mat3ra/workflow-designer, which isn't checked out in this task.
  • The dependencies patch in jsonSchemaPatchConfig being silently dropped (dead code — the preferGridMetric/KPPRA-vs-spacing branching never applies) is a separate bug, filed in log/findings.md.

…given

PointsGridDataProvider(dimensions=X, isEdited=True) -- the notebook path --
never supplied gridMetricValue, so it defaulted to the DEFAULT_KPPRA sentinel
(-1) and got persisted as if it were a deliberate, edited value.

That sentinel then permanently locks the k-grid from further editing in the
UI: the form's own schema requires gridMetricValue >= 1 for KPPRA, RJSF
resends the full form state on every edit, and -1 rides along on every one of
them, failing validation every time with no error shown.

Mirrors PointsGridFormDataProvider.setData() in JS, which already derives the
metric from dimensions whenever a user sets them manually. An explicitly
passed gridMetricValue is still respected; the untouched default path (no
dimensions, no metric) is unaffected.
@VsevolodX VsevolodX changed the title fix: PointsGridDataProvider derives gridMetricValue from dimensions, not the -1 sentinel feature/SOF-7990 fix: PointsGridDataProvider derives gridMetricValue Aug 9, 2026

@exabyte-io-bot exabyte-io-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

2 blocker(s) to resolve before this merges, across 5 finding(s).

Batched findings

  • nit tests/py/context/test_points_grid_data_provider.py:123 — (AGENTS.md 5.1) A couple of small nits, non-blocking: here and on line 132, let's spell out the math (e.g. 4 * 4 * 4 and 4 * 4 * 4 * 2) instead of hardcoding the magic numbers 64 and 128. It makes the test act as self-documenting proof of the logic. Also, in calculate_grid_metric, the units argument is completely unused — let's remove it if the base interface allows it. Happy to approve once those are in.

Generated from Timur Bazhirov's review corpus (12,340 of his own past comments). Severity follows AGENTS-code-review-tb.md; confidence is the model's own estimate.

shifts: List[float] = Field(default_factory=lambda: [0.0, 0.0, 0.0])
gridMetricType: GridMetricType = Field(default=GridMetricType.KPPRA)
gridMetricValue: float = Field(default=DEFAULT_KPPRA)
n_atoms: int = Field(default=1, exclude=True)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

(TB-NAME-1) Let's spell this out — n_atoms uses an abbreviation that violates our naming conventions. We should use number_of_atoms instead. (See AGENTS.md HARD RULE 4).

n_atoms: int = Field(default=1, exclude=True)

@model_validator(mode="after")
def _derive_grid_metric_value_from_dimensions(self) -> "PointsGridDataProvider":

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

(TB-DOC-2) Let's add a docstring here explaining what this validator does and why it's triggered after initialization. All new methods need docstrings.

) -> List[int]:
raise NotImplementedError

def calculate_grid_metric(self, grid_metric_type: str, dimensions: List[int], units: str = "angstrom") -> float:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

(AGENTS.md 1.2. OOP Guidelines & Antipatterns) Why is grid_metric_type typed as a str in the signature but compared to an enum GridMetricType.KPPRA here? Let's ensure the type hint matches the actual usage (e.g. GridMetricType or Union[str, GridMetricType]) to prevent type-checking failures or silent comparison bugs.


def calculate_grid_metric(self, grid_metric_type: str, dimensions: List[int], units: str = "angstrom") -> float:
raise NotImplementedError
if grid_metric_type == GridMetricType.KPPRA:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

(TB-DRY-1) Let's avoid hardcoding the exact 3 indices here. Using math.prod(dimensions) * self.number_of_atoms (assuming n_atoms gets renamed per the other comment) makes this safer for 1D or 2D grids and a bit cleaner to read.

VsevolodX and others added 3 commits August 11, 2026 13:31
PointsGridDataProvider had no material, so it defaulted the atom count to 1
and never emitted reciprocalVectorRatios. Both are properties of the material,
and the JS provider derives both from the one it is always constructed with.

Two consequences, both silent:

- KPPRA is per reciprocal atom, so a 2-atom cell on a 4x4x4 grid recorded 64
  where the correct value is 128 -- wrong by a factor of the atom count.
- The absent reciprocalVectorRatios made the k-grid Important Settings form
  reject every edit without surfacing an error, so a cloned job's grid could
  not be changed.

The provider now takes `material` and derives both into the ESSE schema fields
(not read-time properties, so a schema-driven default_data cannot drop them).
When the atom count cannot be derived it raises rather than assuming one atom.

`material` is typed structurally via a runtime_checkable Protocol: mat3ra-made
ships scipy only under its `tools` extra while importing Material requires it,
so a nominal import would make mat3ra.wode unimportable on a plain install.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
134 chars against ruff's line-length = 120, which reddened run-py-linter.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Inserting `_material_stub` after `import pytest` pushed eight imports below a
function definition, which CI's ruff (0.0.270) flags as E402. Verified clean
with CI's exact invocation: ruff check --line-length=120 --target-version=py310

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

2 participants