Skip to content

Add thermal expansion workflow (CTEMaker) - #1559

Merged
JaGeo merged 31 commits into
materialsproject:mainfrom
hrushikesh-s:cte-workflow
Oct 2, 2026
Merged

JaGeo merged 31 commits into
materialsproject:mainfrom
hrushikesh-s:cte-workflow

Conversation

@hrushikesh-s

@hrushikesh-s hrushikesh-s commented Sep 24, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

This PR adds a workflow for the thermal expansion tensor from third-order force constants. It builds on #1558 and needs its anharmonic fit options. Until #1558 is merged, this PR also shows its changes.

  • New CTEMaker for VASP (atomate2.vasp.flows.cte) and for force fields (atomate2.forcefields.flows.cte), with a shared BaseCTEMaker in atomate2.common.flows.cte.
  • The flow runs a tight relaxation. The relaxed structure then goes to the pheasy PhononMaker and to the existing ElasticMaker, with no second relaxation in either. The two sub-flows do not depend on each other. The elastic fit gets the stress of the relaxation, as in the elastic flow.
  • The pheasy phonon maker runs with cal_anhar_fcs=True, the one-shot fit, 0.03 Å displacements and min_length=12.0. The fit methods are set by anhar_fit_methods of the phonon maker, so the cocktail fit can be used too.
  • New job compute_cte reads the force constants from the pheasy job folder. CTEDocument.from_force_constants then gets the mode Grüneisen tensors from phono3py and computes the thermal expansion tensor from the mode heat capacities, the Grüneisen tensors and the elastic compliance.
  • If a frequency on the q-point mesh is below -tol_imaginary_modes (0.1 THz by default), a warning is raised and the thermal expansion of that fit is not computed.
  • The job checks that the elastic structure has the same lattice and atoms as the phonon unit cell, so both tensors are in the same frame.
  • New output schema CTEDocument, with one CTEResult per fit method. Each result holds the thermal expansion tensor and the volumetric thermal expansion at each temperature, the heat-capacity weighted mean Grüneisen tensor, and the lowest frequency on the mesh.
  • duecredit entries for phonopy and phono3py.
  • A new section in docs/user/codes/vasp.md, with an example and a note on r2SCAN.
  • Tutorial: tutorials/cte_workflow.ipynb runs the workflow with three MACE potentials. Section 1 runs NaCl, KCl, MgO, CaO and GaAs at the default settings and checks the results against finite displacements with phono3py. Section 2 converges the third-order cutoff and the supercell for Si.

Additional dependencies introduced (if any)

  • phono3py>=4.5 is added to the pheasy extra. It computes the mode Grüneisen tensors from the second- and third-order force constants. phono3py 4.5.0 requires phonopy 4.5, so atomate2[pheasy] installs phonopy 4.5.

TODO (if any)

Checklist

Before a pull request can be merged, the following items must be checked:

  • Code is in the standard Python style.
    The easiest way to handle this is to run the following in the correct sequence on
    your local machine. Start with running ruff and ruff format on your new code. This will
    automatically reformat your code to PEP8 conventions and fix many linting issues.
  • Doc strings have been added in the Numpy docstring format.
    Run ruff on your code.
  • Type annotations are highly encouraged. Run mypy to
    type check your code.
  • Tests have been added for any new functionality or bug fixes.
  • All linting and tests pass.

Note that the CI system will run all the above checks. But it will be much more
efficient if you already fix most errors prior to submitting the PR. It is highly
recommended that you use the pre-commit hook provided in the repository. Simply run
pre-commit install and a check will be run prior to allowing commits.

@hrushikesh-s

Copy link
Copy Markdown
Collaborator Author

Hi @JaGeo, we will also share benchmarking results for this PR.
Either @ZKC19940412 or I will post them here.

@JaGeo

JaGeo commented Sep 24, 2026

Copy link
Copy Markdown
Member

@hrushikesh-s is there a connection to the qha Maker that one could exploit?

@hrushikesh-s

hrushikesh-s commented Oct 2, 2026 •

Copy link
Copy Markdown
Collaborator Author

@hrushikesh-s is there a connection to the qha Maker that one could exploit?

I went through the QHA workflow for parts to reuse. The two workflows get the thermal expansion in different ways. QhaMaker runs harmonic phonons at several volumes, and phonopy's QHA fits the free energy against volume. CTEMaker runs one pheasy fit with third-order force constants at one volume, and phono3py gives the mode Grüneisen tensors from them. The thermal expansion tensor then follows from these tensors and the elastic compliance. So the volume scan, the EOS fit and PhonopyQHA have no counterpart in CTEMaker.

CTEMaker already reuses the pheasy PhononMaker, the ElasticMaker, structure_to_primitive and structure_to_conventional, and the PhononDoc type and job folder lookup from the Grüneisen jobs.

@hrushikesh-s

Copy link
Copy Markdown
Collaborator Author

Hi @JaGeo, this is ready for review as well. It builds on #1558 and includes the same --tol change. The benchmarks we promised are in the tutorial, tutorials/cte_workflow.ipynb. Five materials are run with three MACE potentials at the default settings and checked against finite displacements in the same supercell and in larger ones. Si has a full cutoff and supercell convergence study, with finite displacements up to a 10x10x10 supercell. I ran the notebook end-to-end on NERSC's Perlmutter with the final code.

@hrushikesh-s
hrushikesh-s requested a review from JaGeo October 2, 2026 01:14
@hrushikesh-s hrushikesh-s changed the title [WIP] Add thermal expansion workflow (CTEMaker) Add thermal expansion workflow (CTEMaker) Oct 2, 2026
@JaGeo

JaGeo commented Oct 2, 2026

Copy link
Copy Markdown
Member

Let's start with the other one

# Conflicts:
#	docs/user/codes/vasp.md
#	pyproject.toml
Comment thread docs/user/codes/vasp.md Outdated

Alternatively, users can accelerate the calculation of interatomic force constants using the machine-learning-based [Pheasy code](https://doi.org/10.48550/arXiv.2508.01020).
The `pheasy` extra, `pip install "atomate2[pheasy]"`, installs the pheasy version this workflow needs, together with phonopy and ALM.
It also installs phono3py for the thermal expansion workflow.

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.

Should we have an additional field for that? historically, phono3py had lots of installation issues and I would only install it of necessary

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yess valid point. I have now moved phono3py installation out of the pheasy extra into a new phono3py extra!

return np.array(alphas), mean_gruneisen


def _expand_born_to_unitcell(phonon: Phonopy) -> np.ndarray:

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.

This is a specific requirement of this workflow, right? Or do we need this elsewhere as well?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes, this is specific to this workflow.. The other phonon workflows pass the Born charges to phonopy, which maps them onto its cells itself.
phonopy.yaml stores the Born charges for phonopy's primitive cell, but phono3py uses the whole unit cell as its primitive cell. With the default primitive cell the two are the same and the charges are passed through. With use_symmetrized_structure="conventional" the unit cell is larger, so the charges are copied onto the matching unit cell atoms.

Nothing else in atomate2 builds a phono3py object from phonopy output, so I have kept it as a private helper next to the code that uses it...

@JaGeo

JaGeo commented Oct 2, 2026

Copy link
Copy Markdown
Member

Rest looks good. I would make a note in the Workflow itself and the documentation that more testing might be required

@hrushikesh-s

Copy link
Copy Markdown
Collaborator Author

Rest looks good. I would make a note in the Workflow itself and the documentation that more testing might be required

@JaGeo, I have now added this note!

@hrushikesh-s

Copy link
Copy Markdown
Collaborator Author

@JaGeo, does this PR look good now?
Is this ready to merge?

@JaGeo

JaGeo commented Oct 2, 2026 •

Copy link
Copy Markdown
Member

It does look good but from the current workflow ouput, I cannot see if the tests actually run. Can you try to change the verbosity of the pytest outputs so that we can confirm all tests are running?

@hrushikesh-s

@JaGeo

JaGeo commented Oct 2, 2026

Copy link
Copy Markdown
Member

The time difference makes the PRs always a bit challenging 😅.

@hrushikesh-s

hrushikesh-s commented Oct 2, 2026 •

Copy link
Copy Markdown
Collaborator Author

It does look good but from the current workflow ouput, I cannot see if the tests actually run. Can you try to change the verbosity of the pytest outputs so that we can confirm all tests are running?

@hrushikesh-s

Done @JaGeo! I have now added -v to the pytest command of the test-non-ase jobs, so each test now prints its own line.

The time difference makes the PRs always a bit challenging 😅.

Yeah 😅

@JaGeo

JaGeo commented Oct 2, 2026

Copy link
Copy Markdown
Member

thanks! Can you also do that for all forcefield tests? Then, I think we are good to merge 😃

@hrushikesh-s

hrushikesh-s commented Oct 2, 2026 •

Copy link
Copy Markdown
Collaborator Author

thanks! Can you also do that for all forcefield tests? Then, I think we are good to merge 😃

@JaGeo, done!

@JaGeo

JaGeo commented Oct 2, 2026

Copy link
Copy Markdown
Member

@hrushikesh-s thanks! I did not find the tests yet? Could you check that the forcfield ones run?

@JaGeo

JaGeo commented Oct 2, 2026

Copy link
Copy Markdown
Member

Ah, they are in common! but you do use a forcefield right?

@hrushikesh-s

Copy link
Copy Markdown
Collaborator Author

Ah, they are in common! but you do use a forcefield right?

Yes, correct. I have now moved the two force field CTE tests to tests/forcefields/flows/test_cte.py. They only need EMT, so the numpy-limited forcefield job now also installs pheasy, ALM and phono3py and runs them there.
I have kept the other tests of get_cte and the Born charge mapping stay in tests/common/jobs/test_cte.py.

@JaGeo

JaGeo commented Oct 2, 2026

Copy link
Copy Markdown
Member

Just to make sure: the pheasy tests are also running in forcefields, right?

@hrushikesh-s

Copy link
Copy Markdown
Collaborator Author

Just to make sure: the pheasy tests are also running in forcefields, right?

No, they are not... I think they are in common.. Let me fix those as well.

@JaGeo

JaGeo commented Oct 2, 2026

Copy link
Copy Markdown
Member

Thanks and sorry that I overlooked this as well!

@JaGeo

JaGeo commented Oct 2, 2026

Copy link
Copy Markdown
Member

Maybe as an addition: so far, we had all workflows that can be run with focefields in the 'forcefield/flows' folder as well.

@hrushikesh-s

Copy link
Copy Markdown
Collaborator Author

@JaGeo, thanks for checking these issues!
The force field tests of both workflows are now in tests/forcefields/flows. test_pheasy.py runs the force field pheasy PhononMaker end to end with EMT forces for fcc Cu, and test_cte.py does the same for the force field CTEMaker. Both run in the numpy-limited forcefield job.

@JaGeo
JaGeo merged commit c35d865 into materialsproject:main Oct 2, 2026
18 checks passed
@JaGeo

JaGeo commented Oct 2, 2026

Copy link
Copy Markdown
Member

Thank you @hrushikesh-s !

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.

3 participants