Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for having non-displaced atoms in Phonopy routines #2110

Merged

Conversation

tomdemeyere
Copy link
Contributor

Summary of Changes

Should be much better

Checklist

  • I have read the "Guidelines" section of the contributing guide. Don't lie! 😉
  • My PR is on a custom branch and is not named main.
  • I have added relevant, comprehensive unit tests.

Notes

  • Your PR will likely not be merged without proper and thorough tests.
  • If you are an external contributor, you will see a comment from @buildbot-princeton. This is solely for the maintainers.
  • When your code is ready for review, ping one of the active maintainers.

@buildbot-princeton
Copy link
Collaborator

Can one of the admins verify this patch?

Copy link
Member

@Andrew-S-Rosen Andrew-S-Rosen left a comment

Choose a reason for hiding this comment

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

@tomdemeyere: Thank you for taking the time to revisit this. It is certainly a lot cleaner and is very easy to follow now! I have some minor comments below, none of which are anything crazy. Overall, very clever modification to the original approach!

I do just have one question: the additional_atoms is an interesting idea. I'm curious to hear more about how you intend to use this in practice (or see people using it in practice).

In surface catalysis, I am most familiar with the opposite scenario to some degree. To calculate thermochemical corrections, people will invoke weak coupling between adsorbate and the metal surface. They'll then vibrate the adsorbate atoms and, perhaps, a few of the adjacent atoms on the surface while fixing the rest. They'll then use the harmonic approximation (e.g. with ASE utilities) to calculate the corrections. This is implicitly assuming that when you compare the energies of two different states along the reaction coordinate, the vibrational modes of the slab are effectively unchanged.

Here, it looks like it's almost the opposite perspective. I guess that's simply because here you are trying to calculate the phonon spectra of the material? Is that right? If doing so, wouldn't fixing a few adsorbate atoms not really make a big difference compared to the size of the slab?

src/quacc/atoms/phonons.py Outdated Show resolved Hide resolved
src/quacc/atoms/phonons.py Show resolved Hide resolved
src/quacc/atoms/phonons.py Outdated Show resolved Hide resolved
src/quacc/recipes/common/phonons.py Outdated Show resolved Hide resolved
src/quacc/recipes/common/phonons.py Outdated Show resolved Hide resolved
src/quacc/recipes/common/phonons.py Outdated Show resolved Hide resolved
src/quacc/recipes/common/phonons.py Outdated Show resolved Hide resolved
@@ -27,6 +27,7 @@ def phonon_flow(
tuple[tuple[int, int, int], tuple[int, int, int], tuple[int, int, int]] | None
) = None,
displacement: float = 0.01,
additional_atoms: Atoms | None = None,
Copy link
Member

Choose a reason for hiding this comment

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

Note to self: we'll want to add this parameter to the other recipes calling the phonon_subflow (tblite and MLP I think?).

tests/core/atoms/test_phonons.py Outdated Show resolved Hide resolved
src/quacc/atoms/phonons.py Show resolved Hide resolved
@tomdemeyere
Copy link
Contributor Author

Overall additional atoms can be seen as the same to "fixed_atoms". I renamed it to put an emphasis on the fact that these atoms are merely spectators of the Phonons calculation, only having an implicit influence through the forces. When you think about this has nothing to do with "fixing".

If you want to calculate an adsorbate on a slab that way you would just send the adsorbate part to regular atoms and the slab as "additional_atoms".

This could be renamed as "fixed_atoms" as well, I am trying to explore the semantic here

@Andrew-S-Rosen
Copy link
Member

Andrew-S-Rosen commented May 10, 2024

Overall additional atoms can be seen as the same to "fixed_atoms". I renamed it to put an emphasis on the fact that these atoms are merely spectators of the Phonons calculation, only having an implicit influence through the forces. When you think about this has nothing to do with "fixing".

If you want to calculate an adsorbate on a slab that way you would just send the adsorbate part to regular atoms and the slab as "additional_atoms".

This could be renamed as "fixed_atoms" as well, I am trying to explore the semantic here

To clarify, I understand that part. My question was about understanding why and when this is relevant, given that it is the opposite from what is usually invoked in the literature.

Edit: Ah, you are saying the slab would go to additional atoms. Sure, that makes sense. I think that conflicts with what you wrote in one of the docstrings. I think additional_atoms makes sense.

@Andrew-S-Rosen
Copy link
Member

Historically, in such scenarios, I have used ASE's Vibrations module (specifying the atoms to vibrate with indices) and then passed that information into ASE's HarmonicThermo class. However, using phonopy for this seems like it might be even better because it would be possible to run the displacement calculations in parallel rather than sequentially.

At some point, I was going to add HarmonicThermo to quacc.runners.thermochemistry to support this, but the phonopy approach is great.

@Andrew-S-Rosen
Copy link
Member

What if we rename the first positional argument to unfixed_atoms and then additional_atoms as fixed_atoms? Not sure if that helps or hurts...

@tomdemeyere
Copy link
Contributor Author

tomdemeyere commented May 10, 2024

Claude 3 Opus is suggesting (😅):

  1. primary_atoms (instead of atoms) and auxiliary_atoms
  2. atoms and fixed_atoms
  3. displaced_atoms (instead of atoms) and nondisplaced_atoms

In this order of "preference", I might actually lean towards 2, (let's just be pragmatic?)

EDIT: To be fair, 3 looks like your last suggestion but without the double negative of "unfixed", this might be the best option to clearly distinguish between what is being moved and the rest, "nondisplaced_atoms" might just be "fixed_atoms" for simplicity?

@Andrew-S-Rosen
Copy link
Member

Ooh I like 3! It's better than my suggestion because it doesn't accidentally imply a FixAtoms constraint. But I'm okay with 2 if you have a strong opinion about it.

@tomdemeyere
Copy link
Contributor Author

That's a good argument let's go with that, I just added an extra underscore

@@ -92,15 +98,16 @@ def phonon_flow(
parameters=job_params,
decorators=job_decorators,
)
if run_relax:
if run_relax and not non_displaced_atoms:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a little bit problematic, another option is to send both displaced and non_displaced and optimise them and separate them again...

Copy link
Member

@Andrew-S-Rosen Andrew-S-Rosen May 10, 2024

Choose a reason for hiding this comment

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

I see. I mean, I guess we can ask if we really even need a relaxation step beforehand? Presumably one could just call relax_job() before calling the flow and it would be exactly the same (although without a tight force tolerance).

If there's a desire to keep this though, then yeah I think the only route would be to relax displaced_atoms+non_displaced_atoms as a single combined_atoms and then pass in combined_atoms[:len(displaced_atoms)] etc. to phonon_subflow. It does, admittedly, complicate things a bit because it seems weird to relax something that is called "non-displaced", but... 🤷

@Andrew-S-Rosen Andrew-S-Rosen mentioned this pull request May 10, 2024
3 tasks
@Andrew-S-Rosen Andrew-S-Rosen changed the title Demeyere phonopy fixed atoms v2 Add support for having non-displaced atoms in Phonopy routines May 10, 2024
@tomdemeyere
Copy link
Contributor Author

@Andrew-S-Rosen

I did not forget this PR, as you may know I am very busy currently, I will definitely come back to it ASAP!

@Andrew-S-Rosen
Copy link
Member

There is no rush at all :) Good luck with wrapping up the PhD!

@tomdemeyere
Copy link
Contributor Author

I have some more time to do what I like now :)

I added some of your suggestions, namely:

  • The type hinting in the internal _thermo_job
  • Fixed the unnecessary comments
  • Added a test for the get_phonopy_supercell

You will see that I added the non_displaced_atoms object to additional_fields in the phonopy_subflow. I did it because otherwise the result dict does not contain any information about these atoms at all. There might be a better way to do this? I will let you decide.

Copy link

codecov bot commented Oct 6, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 97.42%. Comparing base (7fef05b) to head (3c9bb51).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2110      +/-   ##
==========================================
+ Coverage   97.41%   97.42%   +0.01%     
==========================================
  Files          85       85              
  Lines        3518     3536      +18     
==========================================
+ Hits         3427     3445      +18     
  Misses         91       91              

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

Copy link
Member

@Andrew-S-Rosen Andrew-S-Rosen left a comment

Choose a reason for hiding this comment

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

@tomdemeyere: What a surprise! Thanks for taking care of this. I think it will be a very nice feature.

I left a few very minor comments but am happy to merge thereafter. I think your changes make a lot of sense.

@@ -89,17 +117,25 @@ def _get_forces_subflow(supercells: list[Atoms]) -> list[dict]:
@job
def _thermo_job(
atoms: Atoms,
phonopy: Phonopy,
phonopy,
Copy link
Member

Choose a reason for hiding this comment

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

Looks like we lost a type hint.

Comment on lines 35 to 37
displaced_atoms: Atoms,
force_job: Job,
non_displaced_atoms: Atoms | None = None,
Copy link
Member

Choose a reason for hiding this comment

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

Out of curiosity, what is the motivation to have two separate Atoms objects rather than one Atoms objects for the full thing and specific indices to not displace? It seems like it might be a bit awkward if they are separate Atoms objects because wouldn't the user have to split up their Atoms object into two before calling this function? I'm sure there's a reason for your suggestion --- just trying to pick up on what it is. We may have gone over this a long time ago.

Copy link
Contributor Author

@tomdemeyere tomdemeyere Oct 7, 2024

Choose a reason for hiding this comment

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

In commit "24608c9" I left a comment.

I think we moved out of this approach to the "FixAtoms" approach, but that did not work as well so we ended up here.

Copy link
Member

@Andrew-S-Rosen Andrew-S-Rosen Oct 7, 2024

Choose a reason for hiding this comment

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

@tomdemeyere: Apologies if we're revisiting history here (it's been a while...). I'm not sure the commit shows the comment, but my question is mostly about what if we had an Atoms object as input with a list[int] of indices to not displace. Was there an issue with that approach? If so, we can stick with what you have here.

src/quacc/recipes/common/phonons.py Outdated Show resolved Hide resolved
@@ -31,6 +32,7 @@ def phonon_subflow(
atoms: Atoms,
force_job: Job,
relax_job: Job | None = None,
fixed_atoms: list[int] | None = None,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do we want to come back to this approach?

Copy link
Member

Choose a reason for hiding this comment

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

@tomdemeyere: Ah, we did that before. Sorry for the memory issues..........

Let me read up on what happened before.

Comment on lines 35 to 37
displaced_atoms: Atoms,
force_job: Job,
non_displaced_atoms: Atoms | None = None,
Copy link
Contributor Author

@tomdemeyere tomdemeyere Oct 7, 2024

Choose a reason for hiding this comment

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

In commit "24608c9" I left a comment.

I think we moved out of this approach to the "FixAtoms" approach, but that did not work as well so we ended up here.

@Andrew-S-Rosen
Copy link
Member

Andrew-S-Rosen commented Oct 7, 2024

@tomdemeyere: Sorry about the back-and-forth. Could you remind me the motivation to switch from specifying a list[int] to two Atoms objects?

Otherwise, once the type hint is fixed, I'll merge pending resolution of the above discussion.

@tomdemeyere
Copy link
Contributor Author

@Andrew-S-Rosen I don't really remember, this might be linked to other problems at the time.

It seems that changing to the index approach is rather straightforward, and might make more sense?

@Andrew-S-Rosen
Copy link
Member

Let's give it a go and see what happens! It seems a tad easier on the user in my opinion. Since it should also support negative indexing, that should make things pretty trivial for the user since it's usually the last N indices where an adsorbate is.

@tomdemeyere
Copy link
Contributor Author

@Andrew-S-Rosen Tell me what you think.

I also made sure that the Schema returns the same Atoms object as the one being sent. I also included non_displaced_atoms and displaced_atoms when relevant.

@Andrew-S-Rosen
Copy link
Member

Thanks! This looks great! Happy to merge!

@Andrew-S-Rosen Andrew-S-Rosen enabled auto-merge (squash) October 11, 2024 15:28
@Andrew-S-Rosen Andrew-S-Rosen merged commit e99d41a into Quantum-Accelerators:main Oct 11, 2024
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Development

Successfully merging this pull request may close these issues.

3 participants