Skip to content

(5) Feature/SOF-7845 update: add Relaxation NB#277

Merged
VsevolodX merged 6 commits intomainfrom
feature/SOF-7845
Mar 13, 2026
Merged

(5) Feature/SOF-7845 update: add Relaxation NB#277
VsevolodX merged 6 commits intomainfrom
feature/SOF-7845

Conversation

@VsevolodX
Copy link
Member

@VsevolodX VsevolodX commented Mar 12, 2026

Summary by CodeRabbit

Documentation

  • Added comprehensive guide for structure relaxation workflows, covering environment setup, material configuration, authentication, job submission, and results visualization.
  • Updated introductory materials with navigation link to the new relaxation documentation.

@review-notebook-app
Copy link

Check out this pull request on  ReviewNB

See visual diffs & provide feedback on Jupyter Notebooks.


Powered by ReviewNB

@coderabbitai
Copy link

coderabbitai bot commented Mar 12, 2026

📝 Walkthrough

Walkthrough

This PR introduces a new relaxation workflow notebook and updates the introduction to link to it. The new notebook implements a complete end-to-end workflow for relaxing material structures using Quantum ESPRESSO on the Mat3ra platform, covering environment setup, authentication, material handling, workflow selection, parameter configuration, job orchestration, monitoring, and results retrieval.

Changes

Cohort / File(s) Summary
Workflow Documentation
other/materials_designer/workflows/Introduction.ipynb, other/materials_designer/workflows/relaxation.ipynb
Added new relaxation workflow notebook implementing material structure relaxation with Quantum ESPRESSO, including setup, API authentication, material loading, workflow selection, computation configuration, and job submission. Updated Introduction.ipynb to link to the new notebook.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant Notebook as Jupyter Notebook
    participant API as Mat3ra Platform API
    participant FS as File System
    participant Standata as Standata
    participant QE as Quantum ESPRESSO

    User->>Notebook: Setup environment & config
    Notebook->>Notebook: Install dependencies
    User->>Notebook: Provide credentials
    Notebook->>API: Authenticate
    API-->>Notebook: Auth token
    Notebook->>FS: Load or retrieve material
    Notebook->>API: Save material to platform
    API-->>Notebook: Material ID
    Notebook->>Standata: Retrieve workflow template
    Standata-->>Notebook: Workflow definition
    Notebook->>Notebook: Configure model & params
    Notebook->>API: Create workflow
    API-->>Notebook: Workflow ID
    Notebook->>API: Get compute resources
    API-->>Notebook: Available clusters
    Notebook->>API: Create job
    API->>QE: Submit job
    QE-->>API: Job status
    loop Poll for completion
        Notebook->>API: Check job status
        API-->>Notebook: Status updates
    end
    Notebook->>API: Retrieve results
    API-->>Notebook: Relaxed structure & data
    Notebook->>Notebook: Visualize results
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • timurbazhirov

Poem

🐰 A relaxation workflow so fine,
With Quantum ESPRESSO's calculations divine,
Materials dance through each step with grace,
From setup to results at a steady pace,
The platform now guides with a link to explore! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title references a feature branch (Feature/SOF-7845) and describes adding a Relaxation notebook, which aligns with the main changes of introducing a new relaxation.ipynb file and updating the Introduction notebook with a link to it.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/SOF-7845
📝 Coding Plan
  • Generate coding plan for human review comments

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Ruff (0.15.5)
other/materials_designer/workflows/Introduction.ipynb

Unexpected end of JSON input

other/materials_designer/workflows/relaxation.ipynb

Unexpected end of JSON input


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 and usage tips.

@VsevolodX VsevolodX changed the title Feature/SOF-7845 update: add Relaxation NB (5) Feature/SOF-7845 update: add Relaxation NB Mar 13, 2026
@@ -0,0 +1,587 @@
{
Copy link
Member

@timurbazhirov timurbazhirov Mar 13, 2026

Choose a reason for hiding this comment

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

Get the list of applications


Reply via ReviewNB

@@ -0,0 +1,587 @@
{
Copy link
Member

@timurbazhirov timurbazhirov Mar 13, 2026

Choose a reason for hiding this comment

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

Remove VASP


Reply via ReviewNB

Base automatically changed from feature/SOF-7846 to main March 13, 2026 23:05
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@other/materials_designer/workflows/relaxation.ipynb`:
- Around line 475-478: The code assumes clusters is non-empty and that filtering
by CLUSTER_NAME returns a match; update the selection logic around CLUSTER_NAME
and cluster to guard these cases: first check if clusters is empty and raise or
log a clear error (or return early) if so, then when CLUSTER_NAME is set attempt
to find a match with next(...) into cluster and if that returns None fall back
to a safe default (e.g., clusters[0]) or raise a descriptive error; ensure any
downstream use of cluster (the variable named cluster) only happens after these
checks so cluster is never None.
- Around line 225-227: The code assumes projects[0] exists when fetching the
default project for ACCOUNT_ID; safeguard the access by checking that the
projects list returned from client.projects.list(...) is non-empty before using
projects[0] and assigning project_id; if empty, handle gracefully (e.g.,
log/raise a clear error mentioning ACCOUNT_ID and that no default project was
found, or create/select a project) so the subsequent print and indexing code
that uses project_id does not raise IndexError.
- Around line 582-586: Check that final_structure (the result of
client.properties.get_for_job with
PropertyName.non_scalar.final_structure.value) is non-empty before indexing [0];
if it's empty, raise or log a clear error/handle the incomplete job path instead
of dereferencing index 0. Specifically, guard the access used to compute
relaxed_material_id and the subsequent
Material.create(client.materials.get(...)) call by validating final_structure
and providing a descriptive error or fallback behavior when no final structure
is present.
- Around line 512-523: create_job is returning a List[dict] (per utils/api.py)
but the code treats job_response as a single dict and passes it to
dict_to_namespace then reads job._id; change the call-site to handle the list:
select the intended job dict from the returned list (e.g., first element) before
calling dict_to_namespace, or adjust create_job usage to unpack the single dict;
update variables job_response -> job_list (or similar) and then set job_dict =
job_list[0] (or the correct index) and call dict_to_namespace(job_dict) to
obtain job_id from job._id.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c666cad8-84fb-49fc-a65c-7492bcf004ae

📥 Commits

Reviewing files that changed from the base of the PR and between ba36044 and 8968c6b.

📒 Files selected for processing (2)
  • other/materials_designer/workflows/Introduction.ipynb
  • other/materials_designer/workflows/relaxation.ipynb

Comment on lines +225 to +227
"projects = client.projects.list({\"isDefault\": True, \"owner._id\": ACCOUNT_ID})\n",
"project_id = projects[0][\"_id\"]\n",
"print(f\"\\u2705 Using project: {projects[0]['name']} ({project_id})\")"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Handle missing default project before indexing.

Line 226 assumes projects[0] always exists. If no default project is returned for the selected account, the notebook crashes with IndexError.

Suggested fix
 projects = client.projects.list({"isDefault": True, "owner._id": ACCOUNT_ID})
-project_id = projects[0]["_id"]
-print(f"\u2705 Using project: {projects[0]['name']} ({project_id})")
+if not projects:
+    raise RuntimeError(f"No default project found for account {ACCOUNT_ID}")
+project_id = projects[0]["_id"]
+print(f"\u2705 Using project: {projects[0]['name']} ({project_id})")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@other/materials_designer/workflows/relaxation.ipynb` around lines 225 - 227,
The code assumes projects[0] exists when fetching the default project for
ACCOUNT_ID; safeguard the access by checking that the projects list returned
from client.projects.list(...) is non-empty before using projects[0] and
assigning project_id; if empty, handle gracefully (e.g., log/raise a clear error
mentioning ACCOUNT_ID and that no default project was found, or create/select a
project) so the subsequent print and indexing code that uses project_id does not
raise IndexError.

Comment on lines +475 to +478
"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",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Guard against empty clusters and unmatched CLUSTER_NAME.

Line 478 assumes at least one cluster exists, and Line 481 can receive cluster=None when the name filter misses. Both cases lead to runtime failure later.

Suggested fix
 if CLUSTER_NAME:
     cluster = next((c for c in clusters if CLUSTER_NAME in c["hostname"]), None)
 else:
-    cluster = clusters[0]
+    cluster = clusters[0] if clusters else None
+
+if cluster is None:
+    available = [c["hostname"] for c in clusters]
+    raise ValueError(
+        f"Cluster not found. CLUSTER_NAME={CLUSTER_NAME!r}, available={available}"
+    )
 
 compute = Compute(

Also applies to: 480-485

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@other/materials_designer/workflows/relaxation.ipynb` around lines 475 - 478,
The code assumes clusters is non-empty and that filtering by CLUSTER_NAME
returns a match; update the selection logic around CLUSTER_NAME and cluster to
guard these cases: first check if clusters is empty and raise or log a clear
error (or return early) if so, then when CLUSTER_NAME is set attempt to find a
match with next(...) into cluster and if that returns None fall back to a safe
default (e.g., clusters[0]) or raise a descriptive error; ensure any downstream
use of cluster (the variable named cluster) only happens after these checks so
cluster is never None.

Comment on lines +512 to +523
"job_response = create_job(\n",
" api_client=client,\n",
" materials=[saved_material],\n",
" workflow=workflow,\n",
" project_id=project_id,\n",
" owner_id=ACCOUNT_ID,\n",
" prefix=job_name,\n",
" compute=compute.to_dict(),\n",
")\n",
"\n",
"job = dict_to_namespace(job_response)\n",
"job_id = job._id\n",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

create_job() response is treated as a dict, but helper returns a list.

At Line 522+, the code assumes a single dict (job_response). The helper signature in utils/api.py returns List[dict], so this path can fail when reading _id.

Suggested fix
-job_response = create_job(
+job_responses = create_job(
     api_client=client,
     materials=[saved_material],
     workflow=workflow,
     project_id=project_id,
     owner_id=ACCOUNT_ID,
     prefix=job_name,
     compute=compute.to_dict(),
 )
 
-job = dict_to_namespace(job_response)
+if not job_responses:
+    raise RuntimeError("No jobs were created")
+job_response = job_responses[0]
+job = dict_to_namespace(job_response)
 job_id = job._id
 print("\u2705 Job created successfully!")
 print(f"Job ID: {job_id}")
 display_JSON(job_response)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@other/materials_designer/workflows/relaxation.ipynb` around lines 512 - 523,
create_job is returning a List[dict] (per utils/api.py) but the code treats
job_response as a single dict and passes it to dict_to_namespace then reads
job._id; change the call-site to handle the list: select the intended job dict
from the returned list (e.g., first element) before calling dict_to_namespace,
or adjust create_job usage to unpack the single dict; update variables
job_response -> job_list (or similar) and then set job_dict = job_list[0] (or
the correct index) and call dict_to_namespace(job_dict) to obtain job_id from
job._id.

Comment on lines +582 to +586
"final_structure = client.properties.get_for_job(\n",
" job_id, property_name=PropertyName.non_scalar.final_structure.value\n",
")\n",
"relaxed_material_id = final_structure[0][\"materialId\"]\n",
"relaxed_material = Material.create(client.materials.get(relaxed_material_id))\n",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Check final_structure before dereferencing index 0.

Line 585 assumes a final-structure property is always present. Failed/incomplete jobs can return an empty list and crash here.

Suggested fix
 final_structure = client.properties.get_for_job(
     job_id, property_name=PropertyName.non_scalar.final_structure.value
 )
-relaxed_material_id = final_structure[0]["materialId"]
+if not final_structure:
+    raise RuntimeError(f"No final structure found for job {job_id}")
+relaxed_material_id = final_structure[0]["materialId"]
 relaxed_material = Material.create(client.materials.get(relaxed_material_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
"final_structure = client.properties.get_for_job(\n",
" job_id, property_name=PropertyName.non_scalar.final_structure.value\n",
")\n",
"relaxed_material_id = final_structure[0][\"materialId\"]\n",
"relaxed_material = Material.create(client.materials.get(relaxed_material_id))\n",
final_structure = client.properties.get_for_job(
job_id, property_name=PropertyName.non_scalar.final_structure.value
)
if not final_structure:
raise RuntimeError(f"No final structure found for job {job_id}")
relaxed_material_id = final_structure[0]["materialId"]
relaxed_material = Material.create(client.materials.get(relaxed_material_id))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@other/materials_designer/workflows/relaxation.ipynb` around lines 582 - 586,
Check that final_structure (the result of client.properties.get_for_job with
PropertyName.non_scalar.final_structure.value) is non-empty before indexing [0];
if it's empty, raise or log a clear error/handle the incomplete job path instead
of dereferencing index 0. Specifically, guard the access used to compute
relaxed_material_id and the subsequent
Material.create(client.materials.get(...)) call by validating final_structure
and providing a descriptive error or fallback behavior when no final structure
is present.

@VsevolodX VsevolodX merged commit 3da8519 into main Mar 13, 2026
5 checks passed
@VsevolodX VsevolodX deleted the feature/SOF-7845 branch March 13, 2026 23:21
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