Skip to content

fix(dftbplus): parse all atoms instead of truncating to 4#1037

Open
abhi-0203 wants to merge 2 commits into
deepmodeling:masterfrom
abhi-0203:master
Open

fix(dftbplus): parse all atoms instead of truncating to 4#1037
abhi-0203 wants to merge 2 commits into
deepmodeling:masterfrom
abhi-0203:master

Conversation

@abhi-0203

@abhi-0203 abhi-0203 commented Jul 19, 2026

Copy link
Copy Markdown

Summary

The DFTB+ parser hardcoded flag checks to only read 4 coordinate rows () and 4 force rows (). This silently truncated any system with more than 4 atoms.

Closes #991

Changes

  • Read the atom count from the GenFormat header line (e.g., ) and use it to determine how many coordinate rows to parse
  • For forces, parse all consecutive rows after 'Total Forces' until a non-force line is encountered
  • Added test with 6-atom system to verify the fix

Testing

  • All existing ammonia (4-atom) tests pass
  • New 6-atom test passes
  • Linter passes

Root Cause

The original code used hardcoded flag ranges ( and ) which limited parsing to exactly 4 rows regardless of the actual system size.

Summary by CodeRabbit

  • Bug Fixes

    • Improved DFTB+ output parsing for systems with varying atom counts.
    • Correctly reads all atomic coordinates and force rows, including configurations with more than four atoms.
    • Prevents parsing errors when force sections end with non-numeric lines.
  • Tests

    • Added coverage for six-atom configurations, including symbols, coordinates, forces, and total energy validation.

The DFTB+ parser hardcoded flag checks to only read 4 coordinate rows
(flag in 3,4,5,6) and 4 force rows (flag in 8,9,10,11). This silently
truncated any system with more than 4 atoms.

Fix: read the atom count from the GenFormat header line and use it to
determine how many coordinate/force rows to parse. For forces, parse
all consecutive rows after 'Total Forces' until a non-force line is
encountered.

Closes deepmodeling#991
@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. bug Something isn't working dpdata labels Jul 19, 2026
@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

DFTB+ output parsing now reads geometry rows from the declared atom count and detects force rows dynamically. A six-atom input fixture and unit test verify symbols, coordinates, forces, shapes, and energy.

DFTB+ parser handling

Layer / File(s) Summary
Count-driven coordinate and force parsing
dpdata/formats/dftbplus/output.py
Geometry parsing counts rows from the declared atom total, and force parsing continues while rows match the expected numeric format.
Six-atom fixture and parser validation
tests/dftbplus/dftb_pin_6atoms.hsd, tests/test_dftbplus_6atoms.py
A six-atom DFTB+ configuration and unit test validate parsed symbols, coordinate and force arrays, shapes, and total energy.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main fix: DFTB+ parsing now handles all atoms instead of truncating at four.
Linked Issues check ✅ Passed The parser now derives coordinate and force row counts from the file content and the new 6-atom test covers the reported truncation bug.
Out of Scope Changes check ✅ Passed The changes stay focused on the DFTB+ truncation fix and its corresponding test coverage.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
dpdata/formats/dftbplus/output.py (1)

67-89: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove unused n_forces variable.

The variable n_forces is initialized and incremented, but its value is never used. You can safely remove it to reduce clutter.

♻️ Proposed refactor
     with open_file(fn_2) as f:
         flag = 0
-        n_forces = 0
         for line in f:
             if line.startswith("Total Forces"):
                 flag = 8
                 forces = []
-                n_forces = 0
             elif flag == 8:
                 # First line after "Total Forces" header contains force data
                 # We don't know the count yet, so parse until we hit a non-force line
                 s = line.split()
                 if len(s) >= 4:
                     try:
                         # Try to parse as force row: "index fx fy fz"
                         int(s[0])
                         float(s[1])
                         forces.append([float(s[1]), float(s[2]), float(s[3])])
-                        n_forces += 1
                     except (ValueError, IndexError):
                         # Not a force row, we're done
                         flag = 0
🤖 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 `@dpdata/formats/dftbplus/output.py` around lines 67 - 89, Remove the unused
n_forces variable from the force-parsing logic, including its initialization and
increment, while leaving the forces collection and parsing behavior unchanged.
🤖 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 `@tests/test_dftbplus_6atoms.py`:
- Around line 11-13: Update the sys.path insertion in
tests/test_dftbplus_6atoms.py to add the project’s parent/root directory by
traversing one level above the directory containing __file__, so the subsequent
read_dftb_plus import works during manual execution; alternatively remove the
path manipulation if the test runner already guarantees the project root on
sys.path.
- Around line 19-24: Update test_six_atoms_parsed_correctly to construct both
DFTB+ fixture paths relative to the test module’s __file__ location instead of
relying on the current working directory; preserve the existing read_dftb_plus
arguments and test behavior.

---

Nitpick comments:
In `@dpdata/formats/dftbplus/output.py`:
- Around line 67-89: Remove the unused n_forces variable from the force-parsing
logic, including its initialization and increment, while leaving the forces
collection and parsing behavior unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 42acbcb5-baf5-48b4-b9f6-50055595e1c6

📥 Commits

Reviewing files that changed from the base of the PR and between b3c88c6 and f5f00d0.

⛔ Files ignored due to path filters (1)
  • tests/dftbplus/detailed_6atoms.out is excluded by !**/*.out
📒 Files selected for processing (3)
  • dpdata/formats/dftbplus/output.py
  • tests/dftbplus/dftb_pin_6atoms.hsd
  • tests/test_dftbplus_6atoms.py

Comment on lines +11 to +13
# Add parent directory to path for imports
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from dpdata.formats.dftbplus.output import read_dftb_plus

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix sys.path insertion to correctly point to the parent directory.

The comment states # Add parent directory to path, but os.path.dirname(os.path.abspath(__file__)) points to the tests directory, not its parent. If the project root needs to be added to sys.path for manual test execution, it should traverse one level higher.

Alternatively, if you run your tests exclusively via a runner like pytest from the root directory, this path manipulation can be removed entirely.

🛠️ Proposed fix
 # Add parent directory to path for imports
-sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
+sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
 from dpdata.formats.dftbplus.output import read_dftb_plus
📝 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
# Add parent directory to path for imports
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from dpdata.formats.dftbplus.output import read_dftb_plus
# Add parent directory to path for imports
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from dpdata.formats.dftbplus.output import read_dftb_plus
🤖 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 `@tests/test_dftbplus_6atoms.py` around lines 11 - 13, Update the sys.path
insertion in tests/test_dftbplus_6atoms.py to add the project’s parent/root
directory by traversing one level above the directory containing __file__, so
the subsequent read_dftb_plus import works during manual execution;
alternatively remove the path manipulation if the test runner already guarantees
the project root on sys.path.

Comment on lines +19 to +24
def test_six_atoms_parsed_correctly(self):
"""Verify that all 6 atoms are parsed, not truncated to 4."""
symbols, coord, energy, forces = read_dftb_plus(
"dftbplus/dftb_pin_6atoms.hsd",
"dftbplus/detailed_6atoms.out",
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Use robust path construction for test data files.

Hardcoded relative paths like "dftbplus/..." will fail if the test is executed from a different working directory (e.g., if pytest is run from the project root, it will look for /dftbplus in the root rather than in /tests). Constructing absolute paths relative to __file__ ensures the test passes regardless of the execution directory.

🛠️ Proposed fix
     def test_six_atoms_parsed_correctly(self):
         """Verify that all 6 atoms are parsed, not truncated to 4."""
+        base_dir = os.path.dirname(os.path.abspath(__file__))
         symbols, coord, energy, forces = read_dftb_plus(
-            "dftbplus/dftb_pin_6atoms.hsd",
-            "dftbplus/detailed_6atoms.out",
+            os.path.join(base_dir, "dftbplus", "dftb_pin_6atoms.hsd"),
+            os.path.join(base_dir, "dftbplus", "detailed_6atoms.out"),
         )
📝 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
def test_six_atoms_parsed_correctly(self):
"""Verify that all 6 atoms are parsed, not truncated to 4."""
symbols, coord, energy, forces = read_dftb_plus(
"dftbplus/dftb_pin_6atoms.hsd",
"dftbplus/detailed_6atoms.out",
)
def test_six_atoms_parsed_correctly(self):
"""Verify that all 6 atoms are parsed, not truncated to 4."""
base_dir = os.path.dirname(os.path.abspath(__file__))
symbols, coord, energy, forces = read_dftb_plus(
os.path.join(base_dir, "dftbplus", "dftb_pin_6atoms.hsd"),
os.path.join(base_dir, "dftbplus", "detailed_6atoms.out"),
)
🤖 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 `@tests/test_dftbplus_6atoms.py` around lines 19 - 24, Update
test_six_atoms_parsed_correctly to construct both DFTB+ fixture paths relative
to the test module’s __file__ location instead of relying on the current working
directory; preserve the existing read_dftb_plus arguments and test behavior.

@codspeed-hq

codspeed-hq Bot commented Jul 19, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

⚠️ Unknown Walltime execution environment detected

Using the Walltime instrument on standard Hosted Runners will lead to inconsistent data.

For the most accurate results, we recommend using CodSpeed Macro Runners: bare-metal machines fine-tuned for performance measurement consistency.

✅ 2 untouched benchmarks


Comparing abhi-0203:master (f5f00d0) with master (b3c88c6)

Open in CodSpeed

@codecov

codecov Bot commented Jul 19, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.88889% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.00%. Comparing base (b3c88c6) to head (f5f00d0).

Files with missing lines Patch % Lines
dpdata/formats/dftbplus/output.py 88.88% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1037      +/-   ##
==========================================
- Coverage   87.01%   87.00%   -0.01%     
==========================================
  Files          90       90              
  Lines        8330     8341      +11     
==========================================
+ Hits         7248     7257       +9     
- Misses       1082     1084       +2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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

Labels

bug Something isn't working dpdata size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Code scan] DFTB+ parser truncates coordinates and forces to four atoms

1 participant