Skip to content

feature/SOF 7924#330

Open
VsevolodX wants to merge 9 commits into
mainfrom
feature/SOF-7924
Open

feature/SOF 7924#330
VsevolodX wants to merge 9 commits into
mainfrom
feature/SOF-7924

Conversation

@VsevolodX
Copy link
Copy Markdown
Member

@VsevolodX VsevolodX commented Jun 6, 2026

  • feat: patch qe in
  • update: optimize
  • update: simplify
  • update: simplify
  • update: simplify 2

Summary by CodeRabbit

  • New Features
    • Add utilities to patch Quantum ESPRESSO namelist parameters in workflow unit input templates, with automatic conversion of Python values to QE-compatible formats and ability to apply updates to single units or across subworkflows.
  • Updates
    • Notebooks updated to support applying additional QE input parameters conditionally within relevant workflow steps.
  • Tests
    • Added unit tests covering namelist patching behavior and error handling.
  • Chore
    • Minor project file formatting adjustment.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 6, 2026

Wondering what really moved? Review this PR in Change Stack to inspect semantic changes, definitions, and references.

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: ec6dbb75-2939-4be0-b701-26bfd362d0f5

📥 Commits

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

📒 Files selected for processing (5)
  • other/materials_designer/workflows/relaxation.ipynb
  • other/materials_designer/workflows/valence_band_offset.ipynb
  • pyproject.toml
  • src/py/mat3ra/notebooks_utils/workflow.py
  • tests/py/unit/test_workflow_utils.py
✅ Files skipped from review due to trivial changes (1)
  • pyproject.toml
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/py/mat3ra/notebooks_utils/workflow.py

📝 Walkthrough

Walkthrough

This PR introduces a new module providing utilities to patch Quantum ESPRESSO namelist parameters in workflow input templates. It converts Python values to Fortran syntax, upserts parameters within QE namelist sections via regex, and propagates changes across workflow units and subworkflows.

Changes

QE Namelist Patching for Workflows

Layer / File(s) Summary
Fortran value formatting and namelist content manipulation
src/py/mat3ra/notebooks_utils/workflow.py
Adds imports and _format_to_f90_value to render Python booleans, strings, and numbers as Fortran namelist literals; set_content finds a &SECTION ... / block and replaces or appends key = value lines.
Unit and workflow-level patching APIs
src/py/mat3ra/notebooks_utils/workflow.py
patch_qe_input applies section-parameter updates to a unit's input templates (optional input_name filter) using set_content and raises ValueError if no template matched; patch_workflow_qe_input iterates subworkflows to locate named units and persist patched units.
Notebook wiring to call patch_workflow_qe_input
other/materials_designer/workflows/relaxation.ipynb, other/materials_designer/workflows/valence_band_offset.ipynb
Notebooks import patch_workflow_qe_input, replace local inline QE-input edits with workflow-level patching guarded by if ADDITIONAL_PARAMETERS, and adjust cutoff target unit lists.
Unit tests for set_content
tests/py/unit/test_workflow_utils.py
Adds test_set_content covering successful namelist updates and the error case when a target namelist section is missing.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Exabyte-io/api-examples#293: Related earlier changes to VBO notebook that previously edited diagonalization/mixing_beta inline instead of using patch_workflow_qe_input.

Suggested reviewers

  • timurbazhirov

Poem

🐰 I nibble text and stitch the line,

true/false and quotes align,
Units patched, subworkflows fed,
Namelists sleep in tidy bed.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'feature/SOF 7924' is vague and does not clearly describe the main change; it appears to be a branch name or ticket reference rather than a meaningful summary of the changeset. Use a descriptive title that summarizes the primary change, such as 'Add Quantum ESPRESSO input patching utilities for workflow parameter updates' or similar.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.
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.

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

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

@coderabbitai coderabbitai Bot Jun 6, 2026

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant