Skip to content

feature/SOF 7924 - #330

Merged
VsevolodX merged 13 commits into
mainfrom
feature/SOF-7924
Jun 10, 2026
Merged

feature/SOF 7924#330
VsevolodX merged 13 commits into
mainfrom
feature/SOF-7924

Conversation

@VsevolodX

@VsevolodX VsevolodX commented Jun 6, 2026

Copy link
Copy Markdown
Member
  • feat: patch qe in
  • update: optimize
  • update: simplify
  • update: simplify
  • update: simplify 2

Summary by CodeRabbit

  • New Features
    • Add a utility to patch Quantum ESPRESSO namelist parameters in workflow unit input templates, formatting values for QE and applying updates across units.
  • Updates
    • Notebooks can now conditionally apply additional QE input parameters during workflow modification for relevant units.
  • Tests
    • Added unit tests covering successful patching and error handling when requested namelist sections are missing.
  • Chore
    • Minor project file formatting and dependency list updates.

@coderabbitai

coderabbitai Bot commented Jun 6, 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: 3fc642d2-a3fc-4ae3-8413-d0e6c7f2c300

📥 Commits

Reviewing files that changed from the base of the PR and between d4f6e4d and 87d296f.

📒 Files selected for processing (2)
  • pyproject.toml
  • src/py/mat3ra/notebooks_utils/workflow.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/py/mat3ra/notebooks_utils/workflow.py

📝 Walkthrough

Walkthrough

Adds patch_workflow_qe_input to format and upsert Quantum ESPRESSO namelist parameters across Workflow subworkflows; notebooks are updated to call it when ADDITIONAL_PARAMETERS are present; tests validate successful edits and the error path for missing namelist sections.

Changes

QE Namelist Patching for Workflows

Layer / File(s) Summary
Imports and typing
src/py/mat3ra/notebooks_utils/workflow.py
Adds regex, typing, and Workflow imports used by the patch implementation.
patch_workflow_qe_input implementation
src/py/mat3ra/notebooks_utils/workflow.py
New function that finds &SECTION ... / blocks, formats Python values to Fortran namelist literals, replaces or appends key = value assignments, raises ValueError when a requested namelist section is missing, persists updated templates/units, and returns the modified Workflow.
Notebook wiring to call patch_workflow_qe_input
other/materials_designer/workflows/relaxation.ipynb, other/materials_designer/workflows/valence_band_offset.ipynb
Notebooks import and call patch_workflow_qe_input guarded by if ADDITIONAL_PARAMETERS, remove local inline unit-edit helpers, and adjust cutoff target unit lists.
Unit tests for patch_workflow_qe_input
tests/py/unit/test_workflow_utils.py
Adds parametrized pytest validating expected content fragments after patching and asserting ValueError when a target namelist section is absent.
pyproject optional-deps edits
pyproject.toml
Removes a trailing comma from an optional dependency string and adds mat3ra-wode and mat3ra-standata to test optional-dependencies.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Exabyte-io/api-examples#293: Related earlier VBO notebook edits that adjusted SCF electron parameter handling now refactored to use the new utility.

Suggested reviewers

  • timurbazhirov

Poem

🐰 I nibble text and stitch each line,
true or false become .true. or .false. fine.
Templates opened, keys set right,
Namelists patched through day and night.
Hop—workflow updated, tests pass bright.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is vague and uses a non-descriptive identifier without explaining the actual change or feature being added. Provide a descriptive title that summarizes the main feature or change, such as 'Add patch_workflow_qe_input function for QE parameter patching' or 'Feature: Support QE input parameter patching in workflows'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
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.

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

✨ 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-7924

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.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/py/mat3ra/notebooks_utils/workflow.py (1)

80-85: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fail loudly when a requested unit name is missing.

Lines 80-85 silently skip names that are not found in any subworkflow. A typo in unit_names then turns into a no-op, and the workflow runs with unpatched QE input.

Proposed fix
 def patch_workflow_qe_input(
     workflow,
     parameters: Mapping[str, Mapping[str, object]],
     unit_names: List[str],
     input_name: Optional[str] = None,
 ) -> None:
@@
-    for subworkflow in workflow.subworkflows:
+    matched_units = set()
+    for subworkflow in workflow.subworkflows:
         for unit_name in unit_names:
             if unit := subworkflow.get_unit_by_name(name=unit_name):
                 patch_qe_input(unit, parameters, input_name=input_name)
                 subworkflow.set_unit(unit)
+                matched_units.add(unit_name)
+
+    missing_units = set(unit_names) - matched_units
+    if missing_units:
+        raise ValueError(f"Workflow units not found for QE patch: {sorted(missing_units)}")
🤖 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/workflow.py` around lines 80 - 85, The loop
over workflow.subworkflows silently ignores unit names that aren't found; change
it to detect missing names and raise an error instead of silently continuing:
while iterating the existing loop that calls
subworkflow.get_unit_by_name(name=unit_name), record which unit_names were
successfully found (e.g., a found set) when you call patch_qe_input(unit,
parameters, input_name=input_name) and subworkflow.set_unit(unit), and after the
loop compute missing = set(unit_names) - found and raise a descriptive exception
(ValueError or KeyError) listing the missing unit names so callers fail loudly
when a requested unit name is not present.
🤖 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/workflow.py`:
- Around line 7-9: The code returns raw single-quoted Fortran literals
(f"'{value}'") which breaks on strings containing apostrophes; modify the path
that handles string values (the expression f"'{value}'" in the snippet that
checks isinstance(value, str)) to escape single quotes by doubling them (e.g.,
sanitized = value.replace("'", "''")) and then return f"'{sanitized}'" so
Fortran namelist strings like O'Brien become 'O''Brien'.
- Around line 22-25: The replacement only matches parameters with exact casing;
update the search and substitution to be case-insensitive so keys like
"VDW_CORR" are replaced when param is "vdw_corr": keep the existing
param_pattern (or prepend (?mi) to it) and call re.search(param_pattern, body,
flags=re.IGNORECASE) and re.sub(param_pattern, line, body, flags=re.IGNORECASE)
(or use re.compile(param_pattern, re.IGNORECASE) and call its search/sub
methods) so existing QE keys are matched regardless of case; references:
parameters loop, param_pattern, _format_to_f90_value, body.
- Around line 14-18: The lookup currently uppercases the requested section
(section = section.lstrip("&").upper()) but the regex search uses a
case-sensitive pattern; change this to perform a case-insensitive match instead:
stop forcing section to upper-case (use section = section.lstrip("&")) and pass
the re.IGNORECASE flag to re.search (or add (?i) to the pattern) so the match
against content is case-insensitive when invoking re.search(pattern, content,
flags=re.IGNORECASE); update the pattern variable and the re.search call
accordingly (references: section, pattern, re.search, content).

---

Outside diff comments:
In `@src/py/mat3ra/notebooks_utils/workflow.py`:
- Around line 80-85: The loop over workflow.subworkflows silently ignores unit
names that aren't found; change it to detect missing names and raise an error
instead of silently continuing: while iterating the existing loop that calls
subworkflow.get_unit_by_name(name=unit_name), record which unit_names were
successfully found (e.g., a found set) when you call patch_qe_input(unit,
parameters, input_name=input_name) and subworkflow.set_unit(unit), and after the
loop compute missing = set(unit_names) - found and raise a descriptive exception
(ValueError or KeyError) listing the missing unit names so callers fail loudly
when a requested unit name is not present.
🪄 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: 04fca7e1-4036-4b1d-aa7f-fe38a2547c3e

📥 Commits

Reviewing files that changed from the base of the PR and between 3712fa8 and 70de2fa.

📒 Files selected for processing (1)
  • src/py/mat3ra/notebooks_utils/workflow.py

Comment on lines +7 to +9
if isinstance(value, bool):
return ".true." if value else ".false."
return f"'{value}'" if isinstance(value, str) else str(value)

@coderabbitai coderabbitai Bot Jun 6, 2026

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 | ⚡ Quick win

Escape apostrophes in Fortran string literals.

Line 9 emits raw single-quoted strings, so a value like O'Brien becomes invalid namelist syntax and breaks the patched input.

Proposed fix
 def _format_to_f90_value(value: object) -> str:
     """Format Python value as Fortran namelist value."""
     if isinstance(value, bool):
         return ".true." if value else ".false."
-    return f"'{value}'" if isinstance(value, str) else str(value)
+    if isinstance(value, str):
+        return "'" + value.replace("'", "''") + "'"
+    return str(value)
🤖 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/workflow.py` around lines 7 - 9, The code
returns raw single-quoted Fortran literals (f"'{value}'") which breaks on
strings containing apostrophes; modify the path that handles string values (the
expression f"'{value}'" in the snippet that checks isinstance(value, str)) to
escape single quotes by doubling them (e.g., sanitized = value.replace("'",
"''")) and then return f"'{sanitized}'" so Fortran namelist strings like O'Brien
become 'O''Brien'.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Do we ever expect a string of o'brien in f90 inputs?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n "patch_qe_input\|patch_workflow_qe_input\|_format_to_f90_value" --type py -C 3

Length of output: 339


🏁 Script executed:

#!/bin/bash
cat src/py/mat3ra/notebooks_utils/workflow.py

echo "---"
# Look for any QE-related notebooks or examples
fd -e ipynb -e py | xargs grep -l -i "quantum\|espresso\|qe\|namelist" 2>/dev/null | head -20

Length of output: 4485



@VsevolodX, fair point — in practice, QE namelist string values are things like 'd3_grimme', 'scf', 'gaussian', 'grimme-d3', file paths, and prefixes — none of which would ever contain an apostrophe. The concern is technically valid per Fortran 90 spec but has no realistic trigger in this context. You can disregard this suggestion.


✏️ Learnings added
Learnt from: VsevolodX
Repo: Exabyte-io/api-examples PR: 330
File: src/py/mat3ra/notebooks_utils/workflow.py:7-9
Timestamp: 2026-06-06T04:00:21.774Z
Learning: In `src/py/mat3ra/notebooks_utils/workflow.py`, the `_format_to_f90_value` function formats Python values as Fortran 90 namelist values for Quantum ESPRESSO inputs. String values passed to QE namelists are always identifiers like calculation types ('scf', 'relax'), vdW correction names ('d3_grimme'), smearing types ('gaussian'), file paths, and prefixes — none of which contain apostrophes. Apostrophe-escaping for Fortran string literals is not needed here.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment thread src/py/mat3ra/notebooks_utils/workflow.py Outdated
Comment thread src/py/mat3ra/notebooks_utils/workflow.py Outdated
@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

for unit_name in unit_names:
if unit := subworkflow.get_unit_by_name(name=unit_name):
patch_qe_input(unit, parameters, input_name=input_name)
subworkflow.set_unit(unit)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

LLM suggested even more compressed version, just 23 LOC for function:

import re


def patch_workflow_qe_input(workflow, parameters, unit_names, input_name=None):
    f90 = lambda value: (
        f".{str(value).lower()}." if isinstance(value, bool) else repr(value) if isinstance(value, str) else str(value)
    )
    for subworkflow in workflow.subworkflows:
        for unit_name in unit_names:
            if not (unit := subworkflow.get_unit_by_name(name=unit_name)):
                continue
            for input_item in getattr(unit, "input", []):
                template = input_item.template
                if input_name not in (None, template.name):
                    continue
                content = template.content
                for section, updates in parameters.items():
                    name = section.lstrip("&")
                    match = re.search(rf"(?ims)(^&{re.escape(name)}\s*\n)(.*?)(^/\s*$)", content)
                    if not match:
                        raise ValueError(f"Namelist '&{name.upper()}' not found.")
                    header, body, footer = match.groups()
                    for key, value in updates.items():
                        line, pattern = f"    {key} = {f90(value)}", rf"(?im)^\s*{re.escape(key)}\s*=.*$"
                        body = re.sub(pattern, line, body) if re.search(pattern, body) else f"{body.rstrip()}\n{line}\n"
                    content = content[: match.start()] + header + body + footer + content[match.end() :]
                template.set_content(content)
            subworkflow.set_unit(unit)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

OK, let's keep this one

from typing import List, Mapping, Optional


def _format_to_f90_value(value: object) -> str:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

value should be any

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

🧹 Nitpick comments (1)
src/py/mat3ra/notebooks_utils/workflow.py (1)

7-12: ⚡ Quick win

Consider adding return type annotation.

The function signature is missing an explicit return type. Adding -> None would improve type clarity.

📝 Suggested addition
 def patch_workflow_qe_input(
     workflow: Workflow,
     parameters: Dict[str, Dict[str, object]],
     unit_names: List[str],
     input_name: Optional[str] = None,
-):
+) -> None:
🤖 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/workflow.py` around lines 7 - 12, The function
patch_workflow_qe_input is missing an explicit return type annotation; update
its signature to include -> None to make the return type explicit (e.g., change
def patch_workflow_qe_input(...): to def patch_workflow_qe_input(...) -> None:),
ensuring any callers or type checkers see it returns nothing.
🤖 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.

Nitpick comments:
In `@src/py/mat3ra/notebooks_utils/workflow.py`:
- Around line 7-12: The function patch_workflow_qe_input is missing an explicit
return type annotation; update its signature to include -> None to make the
return type explicit (e.g., change def patch_workflow_qe_input(...): to def
patch_workflow_qe_input(...) -> None:), ensuring any callers or type checkers
see it returns nothing.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8d7fa39e-ce5c-4a21-b460-f1475a757afe

📥 Commits

Reviewing files that changed from the base of the PR and between 37a34c3 and d4f6e4d.

📒 Files selected for processing (2)
  • src/py/mat3ra/notebooks_utils/workflow.py
  • tests/py/unit/test_workflow_utils.py

@VsevolodX
VsevolodX merged commit 1196c83 into main Jun 10, 2026
8 checks passed
@VsevolodX
VsevolodX deleted the feature/SOF-7924 branch June 10, 2026 00:11
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