Skip to content

feature/SOF-7958 Feat: add NEB calculation notebook for QE neb.x#349

Open
VsevolodX wants to merge 13 commits into
mainfrom
feature/SOF-7958
Open

feature/SOF-7958 Feat: add NEB calculation notebook for QE neb.x#349
VsevolodX wants to merge 13 commits into
mainfrom
feature/SOF-7958

Conversation

@VsevolodX

@VsevolodX VsevolodX commented Jul 17, 2026

Copy link
Copy Markdown
Member

Add espresso NEB workflow notebook

Summary by CodeRabbit

  • New Features
    • Added Quantum ESPRESSO NEB workflow notebook (reaction pathways, energy profile, and energy barrier).
    • Added notebooks to create materials sets and to generate NEB path member images in deterministic order.
    • Updated the Materials Designer guide to link the new NEB and materials-set notebooks.
  • Improvements
    • Enhanced materials-set discovery and ordered member handling for workflow inputs.
    • Extended job creation to accept an optional materials-set reference for multi-material runs.
  • Tests
    • Added unit tests covering materials-set discovery/listing, set creation/reuse, and materials-set behavior in job payloads.

@review-notebook-app

Copy link
Copy Markdown

Check out this pull request on  ReviewNB

See visual diffs & provide feedback on Jupyter Notebooks.


Powered by ReviewNB

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6733f743-ffec-4423-aeea-a4c99bd0a0a4

📥 Commits

Reviewing files that changed from the base of the PR and between 85b31be and 730f296.

📒 Files selected for processing (5)
  • other/materials_designer/workflows/neb.ipynb
  • src/py/mat3ra/notebooks_utils/core/entity/job/api.py
  • src/py/mat3ra/notebooks_utils/core/entity/material/api.py
  • tests/py/unit/core/entity/test_job_api.py
  • tests/py/unit/core/entity/test_material_api.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/py/unit/core/entity/test_material_api.py
  • src/py/mat3ra/notebooks_utils/core/entity/material/api.py
  • other/materials_designer/workflows/neb.ipynb

📝 Walkthrough

Walkthrough

Changes

Adds reusable material-set lookup and creation APIs with tests, notebooks for creating sets and generating NEB path materials, convex-hull integration, and a linked Quantum ESPRESSO NEB workflow covering setup, submission, polling, and result visualization.

Materials and NEB workflow

Layer / File(s) Summary
Material-set API and job payload contracts
src/py/mat3ra/notebooks_utils/core/entity/material/api.py, src/py/mat3ra/notebooks_utils/core/entity/job/api.py, tests/py/unit/core/entity/*
Adds entity-set lookup, ordered member retrieval, set creation or reuse, materials-set job references, validation, and unit coverage.
Materials-set creation notebook
other/materials_designer/create_materials_set.ipynb
Loads materials, creates or reuses platform materials, and creates or reuses ordered or unordered materials sets.
NEB path generation and execution
other/materials_designer/create_neb_path_materials.ipynb, other/materials_designer/workflows/neb.ipynb
Generates ordered path images and runs the configured NEB workflow through job submission, polling, and reaction-energy result retrieval.
Navigation and convex-hull integration
other/materials_designer/Introduction.ipynb, other/materials_designer/workflows/Introduction.ipynb, other/materials_designer/workflows/analyze_convex_hull.ipynb
Links the new notebooks and updates convex-hull material discovery to use ordered set members or an API fallback.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Notebook
  participant APIClient
  participant Standata
  participant Compute
  Notebook->>APIClient: authenticate and select account/project
  Notebook->>APIClient: load ordered NEB materials
  Notebook->>Standata: select and save NEB workflow
  Notebook->>APIClient: create and submit NEB job
  APIClient->>Compute: execute NEB calculation
  Notebook->>APIClient: poll completion and retrieve energy results
Loading

Possibly related PRs

Suggested reviewers: timurbazhirov

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly reflects the main change: adding a Quantum ESPRESSO NEB calculation notebook.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/SOF-7958

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@other/materials_designer/workflows/neb.ipynb`:
- Around line 118-124: Update the cell 25 logic that computes and applies
effective_n_images so N_IMAGES is used only when the ordered material set
contains exactly the first and last materials; when intermediate saved materials
exist, leave the image count derived from that set and do not write the explicit
N_IMAGES value into the nImages context. Preserve the default fallback of 1 for
the exactly-two-material case.
- Around line 422-443: Update the cluster selection logic around CLUSTER_NAME
and clusters to validate both cases explicitly: raise a clear, actionable error
when the requested name matches no cluster, and when no clusters are available
before accessing clusters[0]. Preserve the existing matching behavior and
compute initialization for valid selections.
- Around line 526-540: Before indexing profile_data in the reaction energy
profile cell, validate that the job completed successfully and that profile_data
contains the expected result; if the job has an error status or the data is
empty, raise a clear failure message instead of allowing an IndexError. Preserve
the existing visualization and peak-energy calculation for successful jobs.

In `@src/py/mat3ra/notebooks_utils/core/entity/material/api.py`:
- Around line 40-53: Escape material_set_name before using it in the regex
filter within find_material_set, importing re and passing
re.escape(material_set_name) so names are matched as literal substrings. Update
tests/py/unit/core/entity/test_material_api.py lines 46-57 to import re and
expect the escaped value in the mock assertion.

In `@tests/py/unit/core/entity/test_material_api.py`:
- Around line 46-57: Update test_find_material_set_returns_first_match so the
expected name regex uses the escaped form of SET_NAME, matching
find_material_set’s re.escape behavior; preserve the remaining mock payload
assertions unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cab0de58-6da1-4818-8b34-99b9a646e838

📥 Commits

Reviewing files that changed from the base of the PR and between 034bbe0 and 31f18aa.

📒 Files selected for processing (5)
  • other/materials_designer/workflows/Introduction.ipynb
  • other/materials_designer/workflows/analyze_convex_hull.ipynb
  • other/materials_designer/workflows/neb.ipynb
  • src/py/mat3ra/notebooks_utils/core/entity/material/api.py
  • tests/py/unit/core/entity/test_material_api.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • other/materials_designer/workflows/Introduction.ipynb

Comment on lines +118 to +124
"source": [
"# Intermediate count for QE when the set has only first+last (ignored if middle images exist)\n",
"N_IMAGES = None # e.g. 20; defaults to 1 when exactly two materials are in the set\n",
"\n",
"# K-grid for the NEB unit\n",
"NEB_KGRID = [1, 1, 1]"
]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

N_IMAGES is not actually ignored when intermediate images exist.

Cell id=6's comment states N_IMAGES is "ignored if middle images exist," but the logic in cell id=25 only gates the default fallback (= 1) on len(saved_materials) == 2; an explicitly set N_IMAGES is applied unconditionally via if effective_n_images is not None. If a user sets N_IMAGES while the ordered set also has intermediate images, the workflow's nImages context will be set to that value even though the actual number of images comes from the set, contradicting the documented behavior and potentially producing an inconsistent NEB configuration.

🐛 Proposed fix
 effective_n_images = N_IMAGES
 if len(saved_materials) == 2 and effective_n_images is None:
     effective_n_images = 1
-if effective_n_images is not None:
+if len(saved_materials) == 2 and effective_n_images is not None:
     unit_to_modify.add_context(
         {"name": "neb", "isEdited": True, "data": {"nImages": effective_n_images}, "extraData": {}}
     )
     print(f"Using N_IMAGES={effective_n_images}")
+elif effective_n_images is not None:
+    print(f"⚠️ N_IMAGES={effective_n_images} ignored: set already has {len(saved_materials)} images")

Also applies to: 342-369

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@other/materials_designer/workflows/neb.ipynb` around lines 118 - 124, Update
the cell 25 logic that computes and applies effective_n_images so N_IMAGES is
used only when the ordered material set contains exactly the first and last
materials; when intermediate saved materials exist, leave the image count
derived from that set and do not write the explicit N_IMAGES value into the
nImages context. Preserve the default fallback of 1 for the exactly-two-material
case.

Comment on lines +422 to +443
"cell_type": "code",
"execution_count": null,
"id": "31",
"metadata": {},
"outputs": [],
"source": [
"from mat3ra.ide.compute import Compute\n",
"\n",
"# Select cluster: use specified name if provided, otherwise use first available\n",
"if CLUSTER_NAME:\n",
" cluster = next((c for c in clusters if CLUSTER_NAME in c[\"hostname\"]), None)\n",
"else:\n",
" cluster = clusters[0]\n",
"\n",
"compute = Compute(\n",
" cluster=cluster,\n",
" queue=QUEUE_NAME,\n",
" ppn=PPN\n",
")\n",
"print(f\"Using cluster: {compute.cluster.hostname}, queue: {QUEUE_NAME}, ppn: {PPN}\")"
]
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Unhelpful failure when CLUSTER_NAME doesn't match, or when no clusters exist.

If CLUSTER_NAME is set but matches no cluster, cluster silently becomes None, and the later compute.cluster.hostname reference will raise an unclear AttributeError. Similarly, clusters[0] in the else-branch will raise an IndexError if the account/project has no clusters. Both should fail with a clear, actionable message.

🛡️ Proposed fix
+if not clusters:
+    raise ValueError("No clusters available for this account/project.")
+
 if CLUSTER_NAME:
     cluster = next((c for c in clusters if CLUSTER_NAME in c["hostname"]), None)
+    if cluster is None:
+        raise ValueError(
+            f"No cluster matching '{CLUSTER_NAME}'. Available: {[c['hostname'] for c in clusters]}"
+        )
 else:
     cluster = clusters[0]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"cell_type": "code",
"execution_count": null,
"id": "31",
"metadata": {},
"outputs": [],
"source": [
"from mat3ra.ide.compute import Compute\n",
"\n",
"# Select cluster: use specified name if provided, otherwise use first available\n",
"if CLUSTER_NAME:\n",
" cluster = next((c for c in clusters if CLUSTER_NAME in c[\"hostname\"]), None)\n",
"else:\n",
" cluster = clusters[0]\n",
"\n",
"compute = Compute(\n",
" cluster=cluster,\n",
" queue=QUEUE_NAME,\n",
" ppn=PPN\n",
")\n",
"print(f\"Using cluster: {compute.cluster.hostname}, queue: {QUEUE_NAME}, ppn: {PPN}\")"
]
},
"cell_type": "code",
"execution_count": null,
"id": "31",
"metadata": {},
"outputs": [],
"source": [
"from mat3ra.ide.compute import Compute\n",
"\n",
"# Select cluster: use specified name if provided, otherwise use first available\n",
"if not clusters:\n",
" raise ValueError(\"No clusters available for this account/project.\")\n",
"\n",
"if CLUSTER_NAME:\n",
" cluster = next((c for c in clusters if CLUSTER_NAME in c[\"hostname\"]), None)\n",
" if cluster is None:\n",
" raise ValueError(\n",
" f\"No cluster matching '{CLUSTER_NAME}'. Available: {[c['hostname'] for c in clusters]}\"\n",
" )\n",
"else:\n",
" cluster = clusters[0]\n",
"\n",
"compute = Compute(\n",
" cluster=cluster,\n",
" queue=QUEUE_NAME,\n",
" ppn=PPN\n",
")\n",
"print(f\"Using cluster: {compute.cluster.hostname}, queue: {QUEUE_NAME}, ppn: {PPN}\")"
]
},
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@other/materials_designer/workflows/neb.ipynb` around lines 422 - 443, Update
the cluster selection logic around CLUSTER_NAME and clusters to validate both
cases explicitly: raise a clear, actionable error when the requested name
matches no cluster, and when no clusters are available before accessing
clusters[0]. Preserve the existing matching behavior and compute initialization
for valid selections.

Comment thread other/materials_designer/workflows/neb.ipynb
Comment thread src/py/mat3ra/notebooks_utils/core/entity/material/api.py Outdated
Comment thread tests/py/unit/core/entity/test_material_api.py

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/py/mat3ra/notebooks_utils/core/entity/material/api.py`:
- Around line 53-59: Escape material_set_name before using it in the regex
filter within find_material_set, importing re in
src/py/mat3ra/notebooks_utils/core/entity/material/api.py and applying
re.escape(). Update tests/py/unit/core/entity/test_material_api.py lines 62-67
to import re and expect re.escape(MATERIAL_SET_NAME) in the mock assertion.
- Around line 154-168: Validate the existing set’s entitySetType in the reuse
branch of the material-set creation flow before assigning or returning
materials_set. Compare materials_set.get("entitySetType") with the type derived
from is_ordered using ORDERED_ENTITY_SET_TYPE or UNORDERED_ENTITY_SET_TYPE; if
they differ, do not silently reuse the set and instead follow the established
error or replacement behavior, preserving the ordering contract.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ec45480a-6b0c-4e37-b619-d7ad91f320c3

📥 Commits

Reviewing files that changed from the base of the PR and between 31f18aa and 85b31be.

📒 Files selected for processing (7)
  • other/materials_designer/Introduction.ipynb
  • other/materials_designer/create_materials_set.ipynb
  • other/materials_designer/create_neb_path_materials.ipynb
  • other/materials_designer/workflows/analyze_convex_hull.ipynb
  • other/materials_designer/workflows/neb.ipynb
  • src/py/mat3ra/notebooks_utils/core/entity/material/api.py
  • tests/py/unit/core/entity/test_material_api.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • other/materials_designer/workflows/analyze_convex_hull.ipynb
  • other/materials_designer/workflows/neb.ipynb

Comment thread src/py/mat3ra/notebooks_utils/core/entity/material/api.py
Comment on lines +154 to +168
materials_set = _find_existing_materials_set(api_client, owner_id, material_set_name)
if materials_set is None:
entity_set_type = ORDERED_ENTITY_SET_TYPE if is_ordered else UNORDERED_ENTITY_SET_TYPE
set_config = {
"name": material_set_name,
"owner": {"_id": owner_id},
"entitySetType": entity_set_type,
}
materials_set = api_client.materials.create_set(set_config)
print(f"✅ Materials set '{materials_set['name']}' " f"({entity_set_type}, {materials_set['_id']})")
else:
print(
f"♻️ Reusing existing materials set '{materials_set['name']}' "
f"({materials_set.get('entitySetType')}, {materials_set['_id']})"
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Reused set's entitySetType isn't validated against the requested is_ordered.

When an existing set is found by name, the code reuses it unconditionally without checking whether materials_set.get("entitySetType") matches the requested is_ordered. An unordered set (e.g. convex-hull) reused for an ordered NEB path (or vice versa) would silently break the path-ordering contract that downstream NEB notebooks depend on (inSet.index).

🛡️ Proposed fix
     materials_set = _find_existing_materials_set(api_client, owner_id, material_set_name)
     if materials_set is None:
         entity_set_type = ORDERED_ENTITY_SET_TYPE if is_ordered else UNORDERED_ENTITY_SET_TYPE
         set_config = {
             "name": material_set_name,
             "owner": {"_id": owner_id},
             "entitySetType": entity_set_type,
         }
         materials_set = api_client.materials.create_set(set_config)
         print(f"✅ Materials set '{materials_set['name']}' " f"({entity_set_type}, {materials_set['_id']})")
     else:
+        expected_entity_set_type = ORDERED_ENTITY_SET_TYPE if is_ordered else UNORDERED_ENTITY_SET_TYPE
+        if materials_set.get("entitySetType") != expected_entity_set_type:
+            raise ValueError(
+                f"Materials set '{material_set_name}' exists as "
+                f"'{materials_set.get('entitySetType')}', but is_ordered={is_ordered} was requested."
+            )
         print(
             f"♻️  Reusing existing materials set '{materials_set['name']}' "
             f"({materials_set.get('entitySetType')}, {materials_set['_id']})"
         )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
materials_set = _find_existing_materials_set(api_client, owner_id, material_set_name)
if materials_set is None:
entity_set_type = ORDERED_ENTITY_SET_TYPE if is_ordered else UNORDERED_ENTITY_SET_TYPE
set_config = {
"name": material_set_name,
"owner": {"_id": owner_id},
"entitySetType": entity_set_type,
}
materials_set = api_client.materials.create_set(set_config)
print(f"✅ Materials set '{materials_set['name']}' " f"({entity_set_type}, {materials_set['_id']})")
else:
print(
f"♻️ Reusing existing materials set '{materials_set['name']}' "
f"({materials_set.get('entitySetType')}, {materials_set['_id']})"
)
materials_set = _find_existing_materials_set(api_client, owner_id, material_set_name)
if materials_set is None:
entity_set_type = ORDERED_ENTITY_SET_TYPE if is_ordered else UNORDERED_ENTITY_SET_TYPE
set_config = {
"name": material_set_name,
"owner": {"_id": owner_id},
"entitySetType": entity_set_type,
}
materials_set = api_client.materials.create_set(set_config)
print(f"✅ Materials set '{materials_set['name']}' " f"({entity_set_type}, {materials_set['_id']})")
else:
expected_entity_set_type = ORDERED_ENTITY_SET_TYPE if is_ordered else UNORDERED_ENTITY_SET_TYPE
if materials_set.get("entitySetType") != expected_entity_set_type:
raise ValueError(
f"Materials set '{material_set_name}' exists as "
f"'{materials_set.get('entitySetType')}', but is_ordered={is_ordered} was requested."
)
print(
f"♻️ Reusing existing materials set '{materials_set['name']}' "
f"({materials_set.get('entitySetType')}, {materials_set['_id']})"
)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/py/mat3ra/notebooks_utils/core/entity/material/api.py` around lines 154 -
168, Validate the existing set’s entitySetType in the reuse branch of the
material-set creation flow before assigning or returning materials_set. Compare
materials_set.get("entitySetType") with the type derived from is_ordered using
ORDERED_ENTITY_SET_TYPE or UNORDERED_ENTITY_SET_TYPE; if they differ, do not
silently reuse the set and instead follow the established error or replacement
behavior, preserving the ordering contract.

Escape regex special characters in set name queries (H2+H), pass
_materialsSet on NEB job create, and fail clearly when the job does
not finish so Cypress matches the UI NEB path.

Co-authored-by: Cursor <cursoragent@cursor.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.

1 participant