Skip to content

update: add relaxation with MACE#289

Open
VsevolodX wants to merge 6 commits intomainfrom
feature/mace-nb
Open

update: add relaxation with MACE#289
VsevolodX wants to merge 6 commits intomainfrom
feature/mace-nb

Conversation

@VsevolodX
Copy link
Copy Markdown
Member

@VsevolodX VsevolodX commented Apr 6, 2026

Summary by CodeRabbit

  • New Features
    • Added two new Jupyter notebooks for building and relaxing material interfaces with strain-matching algorithms
    • Workflows include material selection, slab construction, interface generation, geometry relaxation via MACE calculator, and interface energy decomposition analysis
    • One notebook offers browser-based execution support via JupyterLite compatibility

@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
Copy link
Copy Markdown

coderabbitai bot commented Apr 6, 2026

Warning

Rate limit exceeded

@VsevolodX has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 18 minutes and 31 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 18 minutes and 31 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3713c06c-636e-405a-b894-0ca28a73b1f9

📥 Commits

Reviewing files that changed from the base of the PR and between 5fa8d44 and f78139b.

📒 Files selected for processing (1)
  • config.yml
📝 Walkthrough

Walkthrough

Two new Jupyter notebooks implement end-to-end workflows for building and relaxing material interfaces using MACE. The standard notebook uses local MACE, while the JupyterLite variant adds Pyodide compatibility patches and browser-based model downloads to run in a browser environment.

Changes

Cohort / File(s) Summary
Core Interface Relaxation Workflow
other/experiments/create_interface_with_relaxation_mace.ipynb
Notebook implementing complete interface relaxation pipeline: material configuration, slab construction with Miller indices and terminations, ZSL strain matching, interface creation, BFGS relaxation with MACE-MP-0 calculator, and interface energy decomposition with interlayer distance analysis.
JupyterLite Interface Relaxation Workflow
other/experiments/jupyterlite/create_interface_with_relaxation_mace.ipynb
Browser-compatible variant of core workflow with Pyodide support. Adds package installation, compatibility shims for torch/matscipy, module stubs for missing compiled dependencies, conditional model downloading via pyodide.http.pyfetch, and helper functions for Pyodide-specific tensor/neighbor-list operations.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Material as Material Database
    participant Slab as Slab Builder
    participant ZSL as ZSL Analyzer
    participant Interface as Interface Creator
    participant MACE as MACE Calculator
    participant Energy as Energy Analyzer

    User->>Material: Load substrate & film
    Material-->>Slab: Materials
    
    User->>Slab: Configure Miller indices, thickness
    Slab->>Slab: Enumerate terminations
    Slab-->>User: Visualize terminations
    
    User->>ZSL: Build slabs & run strain matching
    Slab-->>ZSL: Substrate & film slabs
    ZSL->>ZSL: Generate ZSL candidates
    ZSL-->>User: Plot strain vs area
    
    User->>Interface: Select match & create interface
    ZSL-->>Interface: Chosen match
    Interface-->>User: Visualize interface
    
    User->>MACE: Relax with BFGS + MACE
    MACE->>MACE: Iterative relaxation (stream energies)
    MACE-->>User: Convergence plot
    
    MACE->>Energy: Relaxed structure
    Energy->>Energy: Filter atoms by tag (substrate/film)
    Energy->>Energy: Compute energy components & delta
    Energy-->>User: Report interlayer distances & delta energies
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Suggested reviewers

  • timurbazhirov

Poem

🐰 Hoppity hop through materials bright,
Slabs and films aligned just right,
MACE relaxes with gentle care,
Energy flows through the interfacial air,
Browser magic, two notebooks fair!

🚥 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 'update: add relaxation with MACE' accurately describes the main change: adding two new notebooks that implement material interface relaxation workflows using the MACE calculator.
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
  • Commit unit tests in branch feature/mace-nb

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.

@@ -0,0 +1,654 @@
{
Copy link
Copy Markdown
Member

@timurbazhirov timurbazhirov Apr 6, 2026

Choose a reason for hiding this comment

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

We should make it so that users can either upload files or use from Standata


Reply via ReviewNB

Copy link
Copy Markdown

@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: 1

🧹 Nitpick comments (5)
other/experiments/create_interface_with_relaxation_mace.ipynb (5)

184-184: Remove stray markdown comment from code cell.

Line 184 contains a section header comment ### 7.3. Plot Radial Distribution Functions that appears to be a copy-paste artifact. This is inside section 3.2 and references a non-sequential section number.

🧹 Proposed fix
 from mat3ra.made.tools.build_components.entities.reusable.three_dimensional.supercell.helpers import create_supercell
 from mat3ra.made.tools.analyze.rdf import RadialDistributionFunction
-### 7.3. Plot Radial Distribution Functions
 from utils.plot import plot_rdf
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@other/experiments/create_interface_with_relaxation_mace.ipynb` at line 184,
Remove the stray markdown header line "### 7.3. Plot Radial Distribution
Functions" from the notebook code cell (it is a copy-paste artifact inside
section 3.2); locate the cell containing that exact string and delete that line
(or convert it to an appropriate in-code comment if a note is needed) so the
code cell contains only executable code relevant to section 3.2.

603-603: Area calculation assumes orthorhombic cell.

The formula volume / cell[2,2] only yields the correct interface area for orthorhombic cells. For triclinic or monoclinic cells, use the cross product of lattice vectors:

import numpy as np
area = np.linalg.norm(np.cross(ase_original_interface.cell[0], ase_original_interface.cell[1]))

If ZSL always produces orthorhombic supercells, this may be fine, but the cross-product approach is more robust.

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

In `@other/experiments/create_interface_with_relaxation_mace.ipynb` at line 603,
The area calculation uses volume / ase_original_interface.cell[2, 2] which
assumes an orthorhombic cell; replace it with a robust cross-product approach by
computing the cross product of the first two lattice vectors from
ase_original_interface.cell and taking its norm (use
numpy.linalg.norm/numpy.cross) to set area, e.g. compute area from
ase_original_interface.cell[0] and ase_original_interface.cell[1] so the code
works for triclinic/monoclinic cells as well.

581-583: Update calculate_energy to use non-deprecated API.

Same deprecation applies here—use atoms.calc = calc instead of set_calculator().

♻️ Proposed fix
 def calculate_energy(atoms, calc):
-    atoms.set_calculator(calc)
+    atoms.calc = calc
     return atoms.get_total_energy()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@other/experiments/create_interface_with_relaxation_mace.ipynb` around lines
581 - 583, The function calculate_energy uses the deprecated ASE API call
atoms.set_calculator(calc); update it to assign the calculator directly via
atoms.calc = calc, then call atoms.get_total_energy() as before (reference:
calculate_energy). Replace the set_calculator invocation with the attribute
assignment to avoid deprecation warnings and preserve behavior.

549-549: Fix duplicate section numbering.

Both lines 549 and 569 define section "7.4". The interlayer distance section should likely be "7.3" to maintain sequential ordering.

Also applies to: 569-569

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

In `@other/experiments/create_interface_with_relaxation_mace.ipynb` at line 549,
Update the duplicated section headers by renumbering the two cells currently
labeled "## 7.4. Output interlayer distance before and after relaxation" (and
the other cell at the later occurrence) so the first becomes "## 7.3. Output
interlayer distance before and after relaxation" to restore sequential ordering;
locate the header strings in the notebook (the exact token "## 7.4. Output
interlayer distance before and after relaxation") and change them to "## 7.3..."
for the earlier cell and ensure the later cell remains "## 7.4..." if that is
intended.

466-466: Replace deprecated set_calculator() with direct property assignment.

set_calculator() has been deprecated in ASE since 2020. Use the calc property directly instead.

♻️ Proposed fix

At line 466:

-ase_interface.set_calculator(calculator)
+ase_interface.calc = calculator

At line 496:

-ase_original_interface.set_calculator(calculator)
+ase_original_interface.calc = calculator

Also applies to: 496-496

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

In `@other/experiments/create_interface_with_relaxation_mace.ipynb` at line 466,
Replace deprecated ASE call set_calculator by assigning to the calc attribute:
find usages of ase_interface.set_calculator(calculator) (both instances
referenced) and change them to ase_interface.calc = calculator so the calculator
is set via the current ASE property instead of the deprecated method.
🤖 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/experiments/create_interface_with_relaxation_mace.ipynb`:
- Around line 622-623: The print uses an incorrect conversion (dividing by 0.16)
to convert eV/Ų to J/m²; replace that with the correct conversion by defining a
constant like EV_PER_ANGSQ_TO_J_PER_MSQ = 16.0217 and use
effective_delta_relaxed * EV_PER_ANGSQ_TO_J_PER_MSQ (or multiply directly) in
the f-string that prints effective_delta_relaxed to produce the J/m² value;
update the f-string in the print near the call that prints "Effective relaxed
Delta per area" to use this constant.

---

Nitpick comments:
In `@other/experiments/create_interface_with_relaxation_mace.ipynb`:
- Line 184: Remove the stray markdown header line "### 7.3. Plot Radial
Distribution Functions" from the notebook code cell (it is a copy-paste artifact
inside section 3.2); locate the cell containing that exact string and delete
that line (or convert it to an appropriate in-code comment if a note is needed)
so the code cell contains only executable code relevant to section 3.2.
- Line 603: The area calculation uses volume / ase_original_interface.cell[2, 2]
which assumes an orthorhombic cell; replace it with a robust cross-product
approach by computing the cross product of the first two lattice vectors from
ase_original_interface.cell and taking its norm (use
numpy.linalg.norm/numpy.cross) to set area, e.g. compute area from
ase_original_interface.cell[0] and ase_original_interface.cell[1] so the code
works for triclinic/monoclinic cells as well.
- Around line 581-583: The function calculate_energy uses the deprecated ASE API
call atoms.set_calculator(calc); update it to assign the calculator directly via
atoms.calc = calc, then call atoms.get_total_energy() as before (reference:
calculate_energy). Replace the set_calculator invocation with the attribute
assignment to avoid deprecation warnings and preserve behavior.
- Line 549: Update the duplicated section headers by renumbering the two cells
currently labeled "## 7.4. Output interlayer distance before and after
relaxation" (and the other cell at the later occurrence) so the first becomes
"## 7.3. Output interlayer distance before and after relaxation" to restore
sequential ordering; locate the header strings in the notebook (the exact token
"## 7.4. Output interlayer distance before and after relaxation") and change
them to "## 7.3..." for the earlier cell and ensure the later cell remains "##
7.4..." if that is intended.
- Line 466: Replace deprecated ASE call set_calculator by assigning to the calc
attribute: find usages of ase_interface.set_calculator(calculator) (both
instances referenced) and change them to ase_interface.calc = calculator so the
calculator is set via the current ASE property instead of the deprecated method.
🪄 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: e4098a70-9ec3-499f-bd91-0ca0df8daf3c

📥 Commits

Reviewing files that changed from the base of the PR and between 080c4e4 and 2a46440.

📒 Files selected for processing (1)
  • other/experiments/create_interface_with_relaxation_mace.ipynb

Comment on lines +622 to +623
"print(\n",
" f\"Effective relaxed Delta per area: {effective_delta_relaxed:.4f} eV/Ang^2 ({effective_delta_relaxed / 0.16:.4f} J/m^2)\")"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Incorrect unit conversion factor for eV/Ų to J/m².

The conversion effective_delta_relaxed / 0.16 is incorrect. The correct factor is:

1 eV/Ų = 1.602×10⁻¹⁹ J / 10⁻²⁰ m² ≈ 16.02 J/m²

Dividing by 0.16 yields ~6.25× instead of ~16×, underestimating the result by a factor of ~2.56.

🐛 Proposed fix
 print(
-    f\"Effective relaxed Delta per area: {effective_delta_relaxed:.4f} eV/Ang^2 ({effective_delta_relaxed / 0.16:.4f} J/m^2)\")
+    f\"Effective relaxed Delta per area: {effective_delta_relaxed:.4f} eV/Ang^2 ({effective_delta_relaxed * 16.0217:.4f} J/m^2)\")

Or define the conversion constant for clarity:

EV_PER_ANGSQ_TO_J_PER_MSQ = 16.0217  # 1 eV/Ų = 16.0217 J/m²
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@other/experiments/create_interface_with_relaxation_mace.ipynb` around lines
622 - 623, The print uses an incorrect conversion (dividing by 0.16) to convert
eV/Ų to J/m²; replace that with the correct conversion by defining a constant
like EV_PER_ANGSQ_TO_J_PER_MSQ = 16.0217 and use effective_delta_relaxed *
EV_PER_ANGSQ_TO_J_PER_MSQ (or multiply directly) in the f-string that prints
effective_delta_relaxed to produce the J/m² value; update the f-string in the
print near the call that prints "Effective relaxed Delta per area" to use this
constant.

@@ -0,0 +1,654 @@
{
Copy link
Copy Markdown
Member

@timurbazhirov timurbazhirov Apr 6, 2026

Choose a reason for hiding this comment

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

We should update this to install packages the same way as other NBs - based on config


Reply via ReviewNB

@@ -0,0 +1,654 @@
{
Copy link
Copy Markdown
Member

@timurbazhirov timurbazhirov Apr 6, 2026

Choose a reason for hiding this comment

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

Title "Set Interface Distance"

Explanation: "We calculate the nearest neighbor (NN) distance for each material to inform the interface distance choice as the average between the two NN distance"


Reply via ReviewNB

Copy link
Copy Markdown

@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: 3

🧹 Nitpick comments (5)
other/experiments/create_interface_with_relaxation_mace_pyodide.ipynb (5)

146-164: types module used but not imported in this cell.

patch_mace_for_pyodide() uses types.ModuleType but types is only imported inside the emscripten block in a later cell. This works because of Python's late binding, but is fragile. Consider adding import types at the top of the cell (lines 86-294) where these functions are defined.

Proposed fix - add import at cell start
+import types
+import os
 
 try:
     from pyodide.http import pyfetch as _pyodide_pyfetch
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@other/experiments/create_interface_with_relaxation_mace_pyodide.ipynb` around
lines 146 - 164, The cell defines patch_mace_for_pyodide and uses
types.ModuleType (and other stubs) but never imports the types module in that
cell, which is fragile; add an explicit import types at the top of the cell
where patch_mace_for_pyodide (and related stubs) are defined so types.ModuleType
resolves locally (update the cell that contains patch_mace_for_pyodide,
_internal, _common_utils, and _logging_tensor to include import types).

203-216: Remove duplicate comment block.

The comment explaining Pyodide's WASM torch build limitations appears twice (lines 203-205 and 210-212). Remove the duplicate for clarity.

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

In `@other/experiments/create_interface_with_relaxation_mace_pyodide.ipynb` around
lines 203 - 216, Remove the duplicate explanatory comment about Pyodide's WASM
torch build so it appears only once before the compatibility adjustments; locate
the block where _torch is imported and the assignments to
_torch.Tensor.__array__ and _torch.Tensor.numpy are made (symbols: _torch,
_tensor_array_compat, _np.array, _torch.Tensor.__array__, _torch.Tensor.numpy)
and delete the redundant repeated lines explaining the limitation while keeping
a single copy of that comment immediately above the compatibility code.

305-314: Remove empty code cell.

This cell contains no code. Consider removing it before merging.

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

In `@other/experiments/create_interface_with_relaxation_mace_pyodide.ipynb` around
lines 305 - 314, Remove the empty code cell (id
"a660f174-a0d3-4beb-bc84-8cfc36471281") from the notebook; locate the code cell
with no "source" content and delete it so the notebook contains only meaningful,
non-empty cells before merging.

508-509: Move package installations to the installation section.

orjson and anywidget are installed here in the relaxation cell. For consistency and clarity, move these installations to Section 2 ("Install Packages") with the other micropip.install calls.

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

In `@other/experiments/create_interface_with_relaxation_mace_pyodide.ipynb` around
lines 508 - 509, Remove the two inline package installs await
micropip.install("orjson") and await micropip.install("anywidget") from the
relaxation cell and add the same micropip.install calls into the notebook's
Section 2 "Install Packages" alongside the existing micropip.install lines; keep
the rest of the cell (to_ase, mace_mp calculator creation, plotly_callback,
dyn.attach, dyn.run, energy prints) unchanged so runtime behavior is identical
after moving the installs.

243-253: Remove commented-out stub code or integrate the defined stub.

The _TorchDFTD3CalculatorStub class is defined at lines 128-130 but this integration code is commented out. Since torch-dftd is installed via micropip later, either remove this dead code entirely or uncomment if the stub is actually needed for Pyodide compatibility.

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

In `@other/experiments/create_interface_with_relaxation_mace_pyodide.ipynb` around
lines 243 - 253, The commented-out integration that registers the
_TorchDFTD3CalculatorStub into sys.modules should be resolved: either delete the
dead commented block entirely if torch-dftd is guaranteed to be installed via
micropip, or uncomment and properly register the stub so mace.calculators can
import it (create a ModuleType for "torch_dftd" and
"torch_dftd.torch_dftd3_calculator", set the stub class
_TorchDFTD3CalculatorStub as TorchDFTD3Calculator on the submodule, set __path__
= [] on the package module, and insert both modules into sys.modules). Locate
the commented block referencing _TorchDFTD3CalculatorStub and apply one of these
two fixes consistently.
🤖 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/experiments/create_interface_with_relaxation_mace_pyodide.ipynb`:
- Around line 553-555: Update the duplicate markdown section number by changing
the heading "### 7.4. Calculate Interface Energy" to "### 7.5. Calculate
Interface Energy" so it no longer conflicts with "7.4. Output interlayer
distance"; locate the heading text "### 7.4. Calculate Interface Energy" in the
notebook and edit the numeric prefix to 7.5 (and adjust any subsequent manual
section numbering if present).
- Around line 94-99: The _MACE_MP_URLS mapping contains hardcoded localhost
endpoints for the "small" and "large" keys which will fail in production; update
the _MACE_MP_URLS dictionary to restore the original public GitHub URLs for
"small" and "large" (replace the "http://localhost:8800/..." entries with the
commented GitHub URLs present in the diff) so downloads use the public releases
instead of a local dev server.
- Around line 277-278: The filename sanitization for building local_path is
stripping dots and hyphens which can cause cache collisions; update the logic
that builds filename (the expression assigned to filename derived from
os.path.basename(url)) to allow '.' and '-' (and '_' and alphanumerics) as safe
characters instead of removing them so versions like "MACE_MPtrj_2022.9.model"
remain distinct; ensure the same variable is then used to compute local_path via
os.path.join(cache_dir, filename).

---

Nitpick comments:
In `@other/experiments/create_interface_with_relaxation_mace_pyodide.ipynb`:
- Around line 146-164: The cell defines patch_mace_for_pyodide and uses
types.ModuleType (and other stubs) but never imports the types module in that
cell, which is fragile; add an explicit import types at the top of the cell
where patch_mace_for_pyodide (and related stubs) are defined so types.ModuleType
resolves locally (update the cell that contains patch_mace_for_pyodide,
_internal, _common_utils, and _logging_tensor to include import types).
- Around line 203-216: Remove the duplicate explanatory comment about Pyodide's
WASM torch build so it appears only once before the compatibility adjustments;
locate the block where _torch is imported and the assignments to
_torch.Tensor.__array__ and _torch.Tensor.numpy are made (symbols: _torch,
_tensor_array_compat, _np.array, _torch.Tensor.__array__, _torch.Tensor.numpy)
and delete the redundant repeated lines explaining the limitation while keeping
a single copy of that comment immediately above the compatibility code.
- Around line 305-314: Remove the empty code cell (id
"a660f174-a0d3-4beb-bc84-8cfc36471281") from the notebook; locate the code cell
with no "source" content and delete it so the notebook contains only meaningful,
non-empty cells before merging.
- Around line 508-509: Remove the two inline package installs await
micropip.install("orjson") and await micropip.install("anywidget") from the
relaxation cell and add the same micropip.install calls into the notebook's
Section 2 "Install Packages" alongside the existing micropip.install lines; keep
the rest of the cell (to_ase, mace_mp calculator creation, plotly_callback,
dyn.attach, dyn.run, energy prints) unchanged so runtime behavior is identical
after moving the installs.
- Around line 243-253: The commented-out integration that registers the
_TorchDFTD3CalculatorStub into sys.modules should be resolved: either delete the
dead commented block entirely if torch-dftd is guaranteed to be installed via
micropip, or uncomment and properly register the stub so mace.calculators can
import it (create a ModuleType for "torch_dftd" and
"torch_dftd.torch_dftd3_calculator", set the stub class
_TorchDFTD3CalculatorStub as TorchDFTD3Calculator on the submodule, set __path__
= [] on the package module, and insert both modules into sys.modules). Locate
the commented block referencing _TorchDFTD3CalculatorStub and apply one of these
two fixes consistently.
🪄 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: 157bfcee-8dea-4b2c-9631-873ab3b47231

📥 Commits

Reviewing files that changed from the base of the PR and between 2a46440 and aabb673.

📒 Files selected for processing (1)
  • other/experiments/create_interface_with_relaxation_mace_pyodide.ipynb

Comment on lines +94 to +99
"_MACE_MP_URLS = {\n",
"# \"small\": \"https://github.com/ACEsuit/mace-mp/releases/download/mace_mp_0/2023-12-10-mace-128-L0_energy_epoch-249.model\",\n",
" \"small\": \"http://localhost:8800/2023-12-10-mace-128-L0_energy_epoch-249.model\",\n",
" \"medium\": \"https://github.com/ACEsuit/mace-mp/releases/download/mace_mp_0/2023-12-03-mace-128-L1_epoch-199.model\",\n",
" \"large\": \"http://localhost:8800/MACE_MPtrj_2022.9.model\",\n",
"# \"large\": \"https://github.com/ACEsuit/mace-mp/releases/download/mace_mp_0/MACE_MPtrj_2022.9.model\",\n",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Hardcoded localhost URLs will break production usage.

The "small" and "large" model URLs point to localhost:8800, which is a local development server. Users running this notebook will fail to download these models. The commented-out GitHub URLs should be restored.

Proposed fix to restore GitHub URLs
 _MACE_MP_URLS = {
-#    "small": "https://github.com/ACEsuit/mace-mp/releases/download/mace_mp_0/2023-12-10-mace-128-L0_energy_epoch-249.model",
-        "small": "http://localhost:8800/2023-12-10-mace-128-L0_energy_epoch-249.model",
+    "small": "https://github.com/ACEsuit/mace-mp/releases/download/mace_mp_0/2023-12-10-mace-128-L0_energy_epoch-249.model",
     "medium": "https://github.com/ACEsuit/mace-mp/releases/download/mace_mp_0/2023-12-03-mace-128-L1_epoch-199.model",
-    "large": "http://localhost:8800/MACE_MPtrj_2022.9.model",
-#    "large": "https://github.com/ACEsuit/mace-mp/releases/download/mace_mp_0/MACE_MPtrj_2022.9.model",
+    "large": "https://github.com/ACEsuit/mace-mp/releases/download/mace_mp_0/MACE_MPtrj_2022.9.model",
📝 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
"_MACE_MP_URLS = {\n",
"# \"small\": \"https://github.com/ACEsuit/mace-mp/releases/download/mace_mp_0/2023-12-10-mace-128-L0_energy_epoch-249.model\",\n",
" \"small\": \"http://localhost:8800/2023-12-10-mace-128-L0_energy_epoch-249.model\",\n",
" \"medium\": \"https://github.com/ACEsuit/mace-mp/releases/download/mace_mp_0/2023-12-03-mace-128-L1_epoch-199.model\",\n",
" \"large\": \"http://localhost:8800/MACE_MPtrj_2022.9.model\",\n",
"# \"large\": \"https://github.com/ACEsuit/mace-mp/releases/download/mace_mp_0/MACE_MPtrj_2022.9.model\",\n",
"_MACE_MP_URLS = {\n",
" \"small\": \"https://github.com/ACEsuit/mace-mp/releases/download/mace_mp_0/2023-12-10-mace-128-L0_energy_epoch-249.model\",\n",
" \"medium\": \"https://github.com/ACEsuit/mace-mp/releases/download/mace_mp_0/2023-12-03-mace-128-L1_epoch-199.model\",\n",
" \"large\": \"https://github.com/ACEsuit/mace-mp/releases/download/mace_mp_0/MACE_MPtrj_2022.9.model\",\n",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@other/experiments/create_interface_with_relaxation_mace_pyodide.ipynb` around
lines 94 - 99, The _MACE_MP_URLS mapping contains hardcoded localhost endpoints
for the "small" and "large" keys which will fail in production; update the
_MACE_MP_URLS dictionary to restore the original public GitHub URLs for "small"
and "large" (replace the "http://localhost:8800/..." entries with the commented
GitHub URLs present in the diff) so downloads use the public releases instead of
a local dev server.

Comment on lines +553 to +555
"cell_type": "markdown",
"source": "### 7.4. Calculate Interface Energy",
"metadata": {},
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Duplicate section number: 7.4 is used twice.

"7.4. Output interlayer distance" at line 538 and "7.4. Calculate Interface Energy" at line 554 both use the same section number. Renumber this to 7.5.

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

In `@other/experiments/create_interface_with_relaxation_mace_pyodide.ipynb` around
lines 553 - 555, Update the duplicate markdown section number by changing the
heading "### 7.4. Calculate Interface Energy" to "### 7.5. Calculate Interface
Energy" so it no longer conflicts with "7.4. Output interlayer distance"; locate
the heading text "### 7.4. Calculate Interface Energy" in the notebook and edit
the numeric prefix to 7.5 (and adjust any subsequent manual section numbering if
present).

Copy link
Copy Markdown

@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: 5

♻️ Duplicate comments (2)
other/experiments/jupyterlite/create_interface_with_relaxation_mace.ipynb (2)

262-263: ⚠️ Potential issue | 🟡 Minor

Preserve dots and hyphens in cached model filenames.

Stripping . and - can collapse distinct versioned model files onto the same cache key.

Proposed fix
-    filename = "".join(c for c in os.path.basename(url) if c.isalnum() or c in "_")
+    filename = "".join(c for c in os.path.basename(url) if c.isalnum() or c in "_.-")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@other/experiments/jupyterlite/create_interface_with_relaxation_mace.ipynb`
around lines 262 - 263, The filename sanitization currently removes dots and
hyphens when building the cache key (see the filename construction that filters
chars from os.path.basename(url) and the subsequent local_path using cache_dir),
which can collapse distinct versioned model files; change the character filter
to allow '.' and '-' (e.g., include them in the allowed-chars check alongside
alphanumeric and '_') so that version separators are preserved when creating
filename and local_path.

614-615: ⚠️ Potential issue | 🟡 Minor

Renumber the second 7.4 heading.

There are two subsections labeled 7.4, which makes the relaxation section harder to follow.

Proposed fix
-### 7.4. Calculate Interface Energy
+### 7.5. Calculate Interface Energy

Also applies to: 629-630

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

In `@other/experiments/jupyterlite/create_interface_with_relaxation_mace.ipynb`
around lines 614 - 615, The notebook has two identical subsection headings "##
7.4. Output interlayer distance before and after relaxation" causing duplicate
numbering; locate both occurrences of that heading (and the later duplicate
around the relaxation section) and renumber the second one to the next
sequential heading (e.g., "## 7.5.") and update any subsequent section numbers
accordingly so headings progress uniquely; ensure the visible heading text in
the notebook cells (the "## 7.4..." strings) are changed, and run the notebook
TOC/outline if present to confirm numbering updated.
🧹 Nitpick comments (1)
other/experiments/jupyterlite/create_interface_with_relaxation_mace.ipynb (1)

69-70: Document or align the looser relaxation threshold.

This notebook uses FMAX = 0.05, while other/experiments/create_interface_with_relaxation_mace.ipynb:95-103 uses 0.01. That 5x change makes the relaxed structures and energies non-comparable unless the browser-specific trade-off is called out.

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

In `@other/experiments/jupyterlite/create_interface_with_relaxation_mace.ipynb`
around lines 69 - 70, RELAXATION_PARAMETERS uses FMAX = 0.05 here which is much
looser than the FMAX = 0.01 used in other notebook
create_interface_with_relaxation_mace.ipynb; either align the thresholds or
explicitly document the difference and rationale. Update
RELAXATION_PARAMETERS["FMAX"] to 0.01 to match the other notebook if you need
comparable relaxed structures/energies, or add a clear comment next to
RELAXATION_PARAMETERS (and mention MACE_MODEL if relevant) stating that this
notebook intentionally uses FMAX = 0.05 for faster/browser-friendly relaxations
and that results are not directly comparable to the FMAX = 0.01 runs.
🤖 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/experiments/jupyterlite/create_interface_with_relaxation_mace.ipynb`:
- Around line 69-70: The MACE_MODEL setting is ignored because the code always
uses MODEL_PATHS_MAP["large"] and commented-out entries prevent small/medium
selection; update the code that constructs/loads the MACE calculator to use the
MACE_MODEL variable (e.g., replace direct indexing with
MODEL_PATHS_MAP[MACE_MODEL]) and ensure MODEL_PATHS_MAP contains active entries
for "small"/"medium"/"large" (or validate MACE_MODEL and fall back to a
default), and adjust the calculator instantiation code (where MODEL_PATHS_MAP is
referenced and the calculator or model path is chosen) to respect this variable
and raise a clear error if an unsupported MACE_MODEL is provided.
- Around line 635-636: The code mixes per-atom and per-area quantities and
computes area incorrectly for non-axis-aligned slabs; fix by computing area from
the cell in-plane lattice vectors (area =
norm(cross(ase_original_interface.cell[0], ase_original_interface.cell[1]))) and
then compute all interface energies consistently per area (e.g.,
delta_relaxed_per_area = (relaxed_energy - relaxed_substrate_energy -
relaxed_layer_energy) / (2 * area)); update usages of calculate_delta_energy,
the area variable, and effective_delta_relaxed to use this consistent per-area
value and convert eV/Å^2 to J/m^2 with the correct factor (≈16.021765) instead
of dividing by 0.16.
- Around line 466-483: The notebook uses zsl_analyzer.zsl_match_holders and a
commented-out plot_strain_vs_area call but never validates matches or
selected_index; add a guard: after matches = zsl_analyzer.zsl_match_holders
check if len(matches) == 0 and raise a clear error or early-return message,
optionally re-enable plot_strain_vs_area(matches, PLOT_SETTINGS) if interactive,
and validate selected_index (ensure it's an int and clamp or raise if out of
range against len(matches)); update the cell that sets selected_index and the
downstream code that uses it (references to selected_index and matches) to use
this validation and a safe default behavior.
- Around line 249-259: download_mace_model_pyodide currently returns the
original model identifier when MODEL_PATHS_MAP resolves to a local path (e.g.,
"large") and can return None for the documented default; change the lookup to
preserve the fallback string and return the resolved path for non-HTTP entries:
in download_mace_model_pyodide adjust the map lookup to use
MODEL_PATHS_MAP.get(model or "medium-mpa-0", model or "medium-mpa-0") and when
url is non-HTTP (if url is None or not url.startswith("http")) return url (the
resolved local path) rather than returning model.
- Around line 176-185: The micropip.install calls that attempt to install
"lmdb", "h5py", and "ssl" must not block patch_mace_for_pyodide(): either remove
those micropip.install calls or wrap each in try/except so failures don't abort
the cell; for "ssl" prefer using pyodide.loadPackage("ssl") if truly required.
Ensure you keep the stub creation in patch_mace_for_pyodide() (the loop that
injects ModuleType entries for "lmdb" and "h5py") as the fallback for imports so
patch_mace_for_pyodide() can run even if micropip installation fails.

---

Duplicate comments:
In `@other/experiments/jupyterlite/create_interface_with_relaxation_mace.ipynb`:
- Around line 262-263: The filename sanitization currently removes dots and
hyphens when building the cache key (see the filename construction that filters
chars from os.path.basename(url) and the subsequent local_path using cache_dir),
which can collapse distinct versioned model files; change the character filter
to allow '.' and '-' (e.g., include them in the allowed-chars check alongside
alphanumeric and '_') so that version separators are preserved when creating
filename and local_path.
- Around line 614-615: The notebook has two identical subsection headings "##
7.4. Output interlayer distance before and after relaxation" causing duplicate
numbering; locate both occurrences of that heading (and the later duplicate
around the relaxation section) and renumber the second one to the next
sequential heading (e.g., "## 7.5.") and update any subsequent section numbers
accordingly so headings progress uniquely; ensure the visible heading text in
the notebook cells (the "## 7.4..." strings) are changed, and run the notebook
TOC/outline if present to confirm numbering updated.

---

Nitpick comments:
In `@other/experiments/jupyterlite/create_interface_with_relaxation_mace.ipynb`:
- Around line 69-70: RELAXATION_PARAMETERS uses FMAX = 0.05 here which is much
looser than the FMAX = 0.01 used in other notebook
create_interface_with_relaxation_mace.ipynb; either align the thresholds or
explicitly document the difference and rationale. Update
RELAXATION_PARAMETERS["FMAX"] to 0.01 to match the other notebook if you need
comparable relaxed structures/energies, or add a clear comment next to
RELAXATION_PARAMETERS (and mention MACE_MODEL if relevant) stating that this
notebook intentionally uses FMAX = 0.05 for faster/browser-friendly relaxations
and that results are not directly comparable to the FMAX = 0.01 runs.
🪄 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: 7b368be8-c798-463b-b6a2-d1eccbe32163

📥 Commits

Reviewing files that changed from the base of the PR and between 7e88061 and 5fa8d44.

📒 Files selected for processing (2)
  • other/experiments/jupyterlite/create_interface_with_relaxation_mace.ipynb
  • other/experiments/jupyterlite/uploads/.gitkeep

Comment on lines +69 to +70
"cell_type": "code",
"source": "INTERFACE_DISTANCE = None # gap between substrate and film, in Angstrom, if None, the distance will be set to the sum of the covalent radii of the two materials\nINTERFACE_VACUUM = 10.0 # vacuum over film, in Angstrom\nREDUCE_RESULT_CELL_TO_PRIMITIVE = True\n\nMAX_AREA = 150 # in Angstrom^2\nMAX_AREA_TOLERANCE = 0.09\nMAX_LENGTH_TOLERANCE = 0.05\nMAX_ANGLE_TOLERANCE = 0.02\n\nRELAXATION_PARAMETERS = {\n \"FMAX\": 0.05,\n}\nMACE_MODEL = \"large\" # \"small\", \"medium\", or \"large\" MACE-MP-0 foundation model",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

MACE_MODEL is currently a no-op.

The notebook advertises MACE_MODEL as a user input, but the calculator always loads MODEL_PATHS_MAP["large"]. Changing the parameter has no effect, and the commented-out map entries mean the advertised "small"/"medium" options are not actually wired.

Also applies to: 94-111, 541-543

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

In `@other/experiments/jupyterlite/create_interface_with_relaxation_mace.ipynb`
around lines 69 - 70, The MACE_MODEL setting is ignored because the code always
uses MODEL_PATHS_MAP["large"] and commented-out entries prevent small/medium
selection; update the code that constructs/loads the MACE calculator to use the
MACE_MODEL variable (e.g., replace direct indexing with
MODEL_PATHS_MAP[MACE_MODEL]) and ensure MODEL_PATHS_MAP contains active entries
for "small"/"medium"/"large" (or validate MACE_MODEL and fall back to a
default), and adjust the calculator instantiation code (where MODEL_PATHS_MAP is
referenced and the calculator or model path is chosen) to respect this variable
and raise a clear error if an unsupported MACE_MODEL is provided.

Comment on lines +176 to +185
" # lmdb, orjson, h5py are C-extension packages that cannot be compiled in\n",
" # Emscripten (no subprocess / cc support). They are only used by the\n",
" # training/dataset-loading paths (mace.data.lmdb_dataset,\n",
" # mace.data.hdf5_dataset) which are pulled in by mace.data.__init__ but\n",
" # are never exercised during Pyodide inference. Stubs let the import\n",
" # succeed; any actual call to these APIs would raise at runtime, which is\n",
" # the correct behaviour for an unsupported operation.\n",
" for _pkg in (\"lmdb\", \"h5py\"):\n",
" if _pkg not in sys.modules:\n",
" sys.modules[_pkg] = types.ModuleType(_pkg)\n",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In current Pyodide/micropip, is micropip.install("ssl")a valid way to obtain the standard-librarysslmodule? Areh5py, lmdb, and orjson currently supported/available as Pyodide packages for browser runtimes?

💡 Result:

No, micropip.install("ssl") is not a valid way to obtain the standard-library ssl module. The ssl module is an unvendored standard library module in Pyodide, loadable via pyodide.loadPackage("ssl") or by initializing with fullStdLib: true, but micropip is for third-party packages, not stdlib modules. While docs suggest micropip.install for some unvendored stdlib like hashlib, ssl depends on OpenSSL and sockets, which have limitations in browser environments. h5py: Yes, supported and available (version 3.13.0) as a Pyodide package. lmdb: No, not currently supported/available as a Pyodide package. A PR to add it was closed without merging. orjson: Yes, supported and available as a Pyodide package (added in 0.24.0, included in current versions).

Citations:


🏁 Script executed:

find . -name "create_interface_with_relaxation_mace.ipynb" -type f

Repository: Exabyte-io/api-examples

Length of output: 206


🏁 Script executed:

cat ./other/experiments/jupyterlite/create_interface_with_relaxation_mace.ipynb | head -500

Repository: Exabyte-io/api-examples

Length of output: 24671


🏁 Script executed:

wc -l ./other/experiments/jupyterlite/create_interface_with_relaxation_mace.ipynb

Repository: Exabyte-io/api-examples

Length of output: 146


🏁 Script executed:

sed -n '176,185p' ./other/experiments/jupyterlite/create_interface_with_relaxation_mace.ipynb

Repository: Exabyte-io/api-examples

Length of output: 784


🏁 Script executed:

sed -n '309,313p' ./other/experiments/jupyterlite/create_interface_with_relaxation_mace.ipynb

Repository: Exabyte-io/api-examples

Length of output: 261


Remove blocking micropip.install() calls for unsupported packages.

Lines 309-313 attempt to install lmdb, h5py, and ssl via micropip before patch_mace_for_pyodide() runs. However:

  • lmdb is not available as a Pyodide package and will fail installation
  • ssl is a standard-library module; micropip cannot provide it (use pyodide.loadPackage("ssl") instead if needed)
  • If these installations fail, the cell blocks and patch_mace_for_pyodide() never executes

The stubs created in patch_mace_for_pyodide() (lines 176-185) are designed to handle missing lmdb and h5py at import time. These micropip calls should either be removed or wrapped in try-except to let the patching fallback run.

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

In `@other/experiments/jupyterlite/create_interface_with_relaxation_mace.ipynb`
around lines 176 - 185, The micropip.install calls that attempt to install
"lmdb", "h5py", and "ssl" must not block patch_mace_for_pyodide(): either remove
those micropip.install calls or wrap each in try/except so failures don't abort
the cell; for "ssl" prefer using pyodide.loadPackage("ssl") if truly required.
Ensure you keep the stub creation in patch_mace_for_pyodide() (the loop that
injects ModuleType entries for "lmdb" and "h5py") as the fallback for imports so
patch_mace_for_pyodide() can run even if micropip installation fails.

Comment on lines +249 to +259
" Args:\n",
" model: MACE model name (e.g. \"medium\", \"medium-mpa-0\") or a direct HTTPS URL.\n",
" None defaults to \"medium-mpa-0\".\n",
" Returns:\n",
" Local filesystem path to the downloaded model file.\n",
" \"\"\"\n",
" if _pyodide_pyfetch is None:\n",
" raise RuntimeError(\"pyodide.http.pyfetch unavailable; call mace_mp() directly in standard Python.\")\n",
" url = MODEL_PATHS_MAP.get(model or \"medium-mpa-0\", model)\n",
" if url is None or not url.startswith(\"http\"):\n",
" return model # already a local path\n",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

download_mace_model_pyodide() returns the wrong path for local aliases.

When model="large", url resolves to /drive/packages/models/MACE_MPtrj_2022.9.model, but the non-HTTP branch returns "large" instead of that path. The documented None default is also broken right now because "medium-mpa-0" is not present in MODEL_PATHS_MAP, so the helper falls through to None.

Proposed fix
-    url = MODEL_PATHS_MAP.get(model or "medium-mpa-0", model)
-    if url is None or not url.startswith("http"):
-        return model  # already a local path
+    resolved_model = model or MACE_MODEL
+    url = MODEL_PATHS_MAP.get(resolved_model, resolved_model)
+    if url is None:
+        raise ValueError(f"Unknown MACE model: {resolved_model!r}")
+    if not url.startswith("http"):
+        return url
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@other/experiments/jupyterlite/create_interface_with_relaxation_mace.ipynb`
around lines 249 - 259, download_mace_model_pyodide currently returns the
original model identifier when MODEL_PATHS_MAP resolves to a local path (e.g.,
"large") and can return None for the documented default; change the lookup to
preserve the fallback string and return the resolved path for non-HTTP entries:
in download_mace_model_pyodide adjust the map lookup to use
MODEL_PATHS_MAP.get(model or "medium-mpa-0", model or "medium-mpa-0") and when
url is non-HTTP (if url is None or not url.startswith("http")) return url (the
resolved local path) rather than returning model.

Comment on lines +466 to +483
"cell_type": "code",
"source": "from utils.plot import plot_strain_vs_area\n\nPLOT_SETTINGS = {\n \"HEIGHT\": 600,\n \"X_SCALE\": \"log\",\n \"Y_SCALE\": \"log\",\n}\n\nmatches = zsl_analyzer.zsl_match_holders\nprint(f\"Found {len(matches)} matches\")\n# plot_strain_vs_area(matches, PLOT_SETTINGS)",
"metadata": {
"trusted": true
},
"id": "8682bbecf48aa30b",
"outputs": [],
"execution_count": null
},
{
"cell_type": "markdown",
"source": "### 5.3. Select the Interface\n\nChoose the match index from the plot above (index 0 has the lowest strain).",
"metadata": {},
"id": "245b45e7bb4a7ac3"
},
{
"cell_type": "code",
"source": "selected_index = 0",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

The match-selection flow needs a guardrail.

The plot call is commented out, but the next cell still asks the user to choose an index from that plot. selected_index = 0 is also never validated, so the notebook fails later when there are no matches or the chosen index is out of range.

Proposed fix
 matches = zsl_analyzer.zsl_match_holders
 print(f"Found {len(matches)} matches")
-# plot_strain_vs_area(matches, PLOT_SETTINGS)
+if not matches:
+    raise ValueError(
+        "No ZSL matches found. Relax the matching tolerances or choose different materials."
+    )
+plot_strain_vs_area(matches, PLOT_SETTINGS)
 selected_index = 0
+if not 0 <= selected_index < len(matches):
+    raise IndexError(f"selected_index must be between 0 and {len(matches) - 1}")
📝 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",
"source": "from utils.plot import plot_strain_vs_area\n\nPLOT_SETTINGS = {\n \"HEIGHT\": 600,\n \"X_SCALE\": \"log\",\n \"Y_SCALE\": \"log\",\n}\n\nmatches = zsl_analyzer.zsl_match_holders\nprint(f\"Found {len(matches)} matches\")\n# plot_strain_vs_area(matches, PLOT_SETTINGS)",
"metadata": {
"trusted": true
},
"id": "8682bbecf48aa30b",
"outputs": [],
"execution_count": null
},
{
"cell_type": "markdown",
"source": "### 5.3. Select the Interface\n\nChoose the match index from the plot above (index 0 has the lowest strain).",
"metadata": {},
"id": "245b45e7bb4a7ac3"
},
{
"cell_type": "code",
"source": "selected_index = 0",
"cell_type": "code",
"source": "from utils.plot import plot_strain_vs_area\n\nPLOT_SETTINGS = {\n \"HEIGHT\": 600,\n \"X_SCALE\": \"log\",\n \"Y_SCALE\": \"log\",\n}\n\nmatches = zsl_analyzer.zsl_match_holders\nprint(f\"Found {len(matches)} matches\")\nif not matches:\n raise ValueError(\n \"No ZSL matches found. Relax the matching tolerances or choose different materials.\"\n )\nplot_strain_vs_area(matches, PLOT_SETTINGS)",
"metadata": {
"trusted": true
},
"id": "8682bbecf48aa30b",
"outputs": [],
"execution_count": null
},
{
"cell_type": "markdown",
"source": "### 5.3. Select the Interface\n\nChoose the match index from the plot above (index 0 has the lowest strain).",
"metadata": {},
"id": "245b45e7bb4a7ac3"
},
{
"cell_type": "code",
"source": "selected_index = 0\nif not 0 <= selected_index < len(matches):\n raise IndexError(f\"selected_index must be between 0 and {len(matches) - 1}\")",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@other/experiments/jupyterlite/create_interface_with_relaxation_mace.ipynb`
around lines 466 - 483, The notebook uses zsl_analyzer.zsl_match_holders and a
commented-out plot_strain_vs_area call but never validates matches or
selected_index; add a guard: after matches = zsl_analyzer.zsl_match_holders
check if len(matches) == 0 and raise a clear error or early-return message,
optionally re-enable plot_strain_vs_area(matches, PLOT_SETTINGS) if interactive,
and validate selected_index (ensure it's an int and clamp or raise if out of
range against len(matches)); update the cell that sets selected_index and the
downstream code that uses it (references to selected_index and matches) to use
this validation and a safe default behavior.

Comment on lines +635 to +636
"cell_type": "code",
"source": "def filter_atoms_by_tag(atoms, material_index):\n return atoms[atoms.get_tags() == material_index]\n\n\ndef calculate_energy(atoms, calc):\n atoms.set_calculator(calc)\n return atoms.get_total_energy()\n\n\ndef calculate_delta_energy(total_energy, *component_energies):\n return total_energy - sum(component_energies)\n\n\nsubstrate_original = filter_atoms_by_tag(ase_original_interface, SUBSTRATE_INDEX)\nlayer_original = filter_atoms_by_tag(ase_original_interface, FILM_INDEX)\nsubstrate_relaxed = filter_atoms_by_tag(ase_final_interface, SUBSTRATE_INDEX)\nlayer_relaxed = filter_atoms_by_tag(ase_final_interface, FILM_INDEX)\n\noriginal_substrate_energy = calculate_energy(substrate_original, calculator)\noriginal_layer_energy = calculate_energy(layer_original, calculator)\nrelaxed_substrate_energy = calculate_energy(substrate_relaxed, calculator)\nrelaxed_layer_energy = calculate_energy(layer_relaxed, calculator)\n\ndelta_original = calculate_delta_energy(original_energy, original_substrate_energy, original_layer_energy)\ndelta_relaxed = calculate_delta_energy(relaxed_energy, relaxed_substrate_energy, relaxed_layer_energy)\n\narea = ase_original_interface.get_volume() / ase_original_interface.cell[2, 2]\nn_interface = ase_final_interface.get_global_number_of_atoms()\nn_substrate = substrate_relaxed.get_global_number_of_atoms()\nn_layer = layer_relaxed.get_global_number_of_atoms()\neffective_delta_relaxed = (\n relaxed_energy / n_interface\n - (relaxed_substrate_energy / n_substrate + relaxed_layer_energy / n_layer)\n ) / (2 * area)\n\nprint(f\"Original Substrate energy: {original_substrate_energy:.4f} eV\")\nprint(f\"Relaxed Substrate energy: {relaxed_substrate_energy:.4f} eV\")\nprint(f\"Original Layer energy: {original_layer_energy:.4f} eV\")\nprint(f\"Relaxed Layer energy: {relaxed_layer_energy:.4f} eV\")\nprint(\"\\nDelta between interface energy and sum of component energies\")\nprint(f\"Original Delta: {delta_original:.4f} eV\")\nprint(f\"Relaxed Delta: {delta_relaxed:.4f} eV\")\nprint(f\"Original Delta per area: {delta_original / area:.4f} eV/Ang^2\")\nprint(f\"Relaxed Delta per area: {delta_relaxed / area:.4f} eV/Ang^2\")\nprint(f\"Relaxed interface energy: {relaxed_energy:.4f} eV\")\nprint(\n f\"Effective relaxed Delta per area: {effective_delta_relaxed:.4f} eV/Ang^2 ({effective_delta_relaxed / 0.16:.4f} J/m^2)\")",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

The final interface-energy normalization is not physically consistent.

This cell mixes a total-energy density (delta_relaxed / area) with effective_delta_relaxed, which is built from per-atom energies and then still labeled eV/Ang^2. On top of that, area = volume / cell[2, 2] only works when the slab cell is axis-aligned. That can silently misreport the headline interface-energy number for generic interfaces.

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

In `@other/experiments/jupyterlite/create_interface_with_relaxation_mace.ipynb`
around lines 635 - 636, The code mixes per-atom and per-area quantities and
computes area incorrectly for non-axis-aligned slabs; fix by computing area from
the cell in-plane lattice vectors (area =
norm(cross(ase_original_interface.cell[0], ase_original_interface.cell[1]))) and
then compute all interface energies consistently per area (e.g.,
delta_relaxed_per_area = (relaxed_energy - relaxed_substrate_energy -
relaxed_layer_energy) / (2 * area)); update usages of calculate_delta_energy,
the area variable, and effective_delta_relaxed to use this consistent per-area
value and convert eV/Å^2 to J/m^2 with the correct factor (≈16.021765) instead
of dividing by 0.16.

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