Skip to content

Vasp hdf5 import - #522

Draft
ajjackson wants to merge 33 commits into
masterfrom
vasp-h5
Draft

Vasp hdf5 import#522
ajjackson wants to merge 33 commits into
masterfrom
vasp-h5

Conversation

@ajjackson

@ajjackson ajjackson commented Aug 4, 2026

Copy link
Copy Markdown
Member

Recent (relatively-speaking) versions of VASP have improved phonon calculation features and a machine-friendly .h5 file output. This makes it a lot more attractive to include some built-in support. A tricky point is that various calculation types will produce an identically-named .h5 file so a bit of intelligence is needed to identify if relevant information is available:

  • An IBRION=[5-8] calculation will produce a Hessian for the whole simulation cell. VASP might identify the supercell nature and use this to reduce the number of displacements, but we still get a 3Nx3N set of force constants. For now we do not look for a primitive cell in this case, and treat the system as though it is primitive. That will give correct DOS and INS simulations for a supercell calculation, but at increased cost compared with a more sophisticated scheme. (Band structures will include folding and incorrect symmetry labels.)

  • If QPOINTS are provided and PHON_DOS is set, VASP will do q-point interpolation and produce a dataset with defined primitive cell and sufficient data to construct QpointPhononModes. The force constants may be present, but can also be missing if the file was produced in a "post-processing" run on an existing .h5 file.

  • If both force constants and primitive cell are defined, we can attempt to map the force constants to primitive as we do for Phonopy. Less metadata is available so for now we naively try to solve for the supercell matrix. This might go wrong in non-trivial cases, but so do some Phonopy imports at the moment; probably they can be fixed together in future. In (fairly common) trivial diagonal supercell cases we should be fine.

https://vasp.at/wiki/Computing_the_phonon_dispersion_and_DOS

h5py is an optional dependency included in the existing phonopy-reader dependency group, so we re-use that in packaging/CI infrastructure. (phonopy-reader also includes YAML, but VASP doesn't need it.)

At the moment I've put the tests in a new vasp_reader group. But perhaps life would be simpler if they shared phonopy_reader.

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Test Results

    42 files   -     37      42 suites   - 37   25m 9s ⏱️ - 26m 25s
 1 188 tests +    30   1 182 ✅ +    30    6 💤 ±  0  0 ❌ ±0 
18 898 runs   - 19 104  18 646 ✅  - 18 887  252 💤  - 217  0 ❌ ±0 

Results for commit ca328e8. ± Comparison against base commit d533d32.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

☂️ Python Coverage

current status: ✅

Overall Coverage

Lines Covered Coverage Threshold Status
39623 29152 74% 0% 🟢

New Files

No new covered files...

Modified Files

No covered modified files...

updated for commit: a1086da by action🐍

@ajjackson

Copy link
Copy Markdown
Member Author

I note that coverage reporting seems to be totally broken. This isn't even a fork and it's still failing to pick up the changed files correctly 😞

- use format_error for hdf5 availability error
- open hdf5 with context manager
- no single-character variable names
- tweak docstrings
- TypedDict for crystal dict output
- Use h5py feature to guarantee string output (not bytes)
- Fix POMASS priority order
- Fix fractional coordinate rounding
- Force amu/Ang units, simplifying params
In an NSW=0 calculation the positions are not a result so should be
read from input. This occurs for post-processing QPOINTS runs. If
there _is_ a structure in results we should use it as the stucture
may have been optimised.
Don't quietly pass onto POTCAR data if INCAR data is bad.
- This introduced a nasty challenge: the final structure in
  results/positions can include finite displacements from Hessian
  calculation. Clearly we don't want that in our reference
  structure. For now fallback to the input structure, but that also
  has possible problems...
Testing suggests that ibrion=6 calculations don't actually do
optimisation to update the base positions, but they do tend to leave a
displaced structure in "results".

So it seems safest to always use the input structure if we didn't get
a primitive transformation; that also streamlines the code a bit.
@github-actions

Copy link
Copy Markdown
Contributor

Coverage report

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  euphonic
  force_constants.py
  qpoint_frequencies.py
  qpoint_phonon_modes.py
  euphonic/readers
  phonopy.py 29
  vasp.py
Project Total  

This report was generated by python-coverage-comment-action

@ajjackson

Copy link
Copy Markdown
Member Author

Woohoo, rebased and the new coverage reporter seems to be working 🎉

Comment thread euphonic/readers/vasp.py
# 1. Try active input/incar/POMASS
if 'input/incar/POMASS' in h5_file:
val = h5_file['input/incar/POMASS'].asstr()[()]
raw_vals = re.findall(r'[0-9.]+', str(val))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Does this need casting to str again?

Also, would it be "better" to use finditer, build the list from that, then check whether the list has elements before returning?

Comment thread euphonic/readers/vasp.py
Comment on lines +154 to +157
match = re.search(
r'POMASS\s*=\s*(?P<masses>[0-9.\s,]+)', incar_content
)
if match:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
match = re.search(
r'POMASS\s*=\s*(?P<masses>[0-9.\s,]+)', incar_content
)
if match:
if not (match := re.search(
r'POMASS\s*=\s*(?P<masses>[0-9.\s,]+)', incar_content
)):

Comment thread euphonic/readers/vasp.py
Comment on lines +171 to +172
matches = re.findall(r'POMASS\s*=\s*(?P<mass>[0-9.]+)', potcar_content)
if matches:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
matches = re.findall(r'POMASS\s*=\s*(?P<mass>[0-9.]+)', potcar_content)
if matches:
if matches := re.findall(r'POMASS\s*=\s*(?P<mass>[0-9.]+)', potcar_content):

Comment thread euphonic/readers/vasp.py


def _read_cell_from_group(
h5_file: 'h5py.File', group_path: str

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Do you need forward declaration if it's in a TYPE_CHECKING block?

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