kim-api-v2 plans: request for comments

Dear KIM community:

We are contacting you because we would like to draw on your insight and experience in interatomic modeling and simulation in order to make a decision about the direction of the next major release (v2) of the kim-api package.

Our experience with using and supporting the current kim-api-v1 package has shown that the package’s complexity (in terms of the number of different “modes of operation”; e.g., neighbor lists: half, full, iterator, locator; NBCs: cluster, miopbc, neigh_pure, neigh_rvec; etc.) makes it difficult for new users to become competent kim-api code developers. Further, the kim-api’s complexity also results in Model implementations that are overly intricate due to the developer’s desire to support all modes of operation. This experience has led us to consider an approach that would have kim-api-v2 take a dramatically simplified form (at least, with regard to neighbor lists and NBCs), while still retaining a high degree of flexibility.

So, we would like your feedback on the following three ideas for kim-api-v2:

(1) Neighbor Lists: Only full, random-access neighbor lists will be supported. We would introduce support for multiple neighbor lists (with different cutoffs), but would remove support for half lists and for iterator-access to the neighbor lists.

(2) Just NEIGH_PURE: All kim-api simulations would be of the NEIGH_PURE type, where there are “contributing” particles and “non-contributing” (ghost) particles. The particle status (contributing or non-contributing) would be specified by an array of integers (as opposed to the kim-api-v1 approach of non-contributing particles having zero neighbors in the neighbor list).

(3) Clear definitions for Energy and Force: This is the same as in kim-api-v1, but we want to be very explicit about the definitions. For a given configuration of particles, C, the configuration’s energy, E^C, is the sum of the particle-energies for its contributing particles. That is, E^C = \Sum_i E_i, where the sum is over just the contributing particles in C and E_i is the energy of particle i. Similarly, the kim-api defines the (partial) force on particle j (contributing or non-contributing) in C as F^C_j = - \frac{\partial E^C}{\partial x_j}, where x_j is the position vector of particle j. These definitions allow for a convenient implementation of domain-decomposition methods and periodic boundary conditions.

We are very interested in your thoughts about these ideas. For example, does this approach make it impossible to perform certain types of simulations? What are the pluses/minuses of these ideas? Are there different ideas that would better achieve our goals of simplicity and flexibility?

Please reply to this message to post your response to the [email protected] mailing list.

Sincerely,

Ryan S. Elliott

PS: Attached is a document containing a more detailed sketch of our current thoughts for kim-api-v2.

kim-api-v2.0.0-proposal-attachment.txt (4.41 KB)

I agree with the general philosophy of prioritizing simplicity over competing considerations, such as performance. I have added some specific comments below.

Aidan

I think #1 and #2 are good ideas towards the simplification of the kim-api-v2. My preference was for NEIGH-PURE-F for my model/driver and they did the job just fine.

However, I wonder about #3, if that might add some confusion. As long as the specific definition of “configuration energy” does not remove any flexibility from the potential developer, as far as a total energy or energy per atom is concerned, then I imagine it could be a good addition.

Dear Ryan,

Sorry about the late answer - and thanks for the reminder (holiday period and all!).

I think simplifying the API is a very good idea indeed, even if it costs a bit on performance. However, performance is important for MD, and the cost must not be too great.

* Removing support for iterator-access to the neighbor lists is a very good idea, it gives nothing that random access does give, and the performance gain is insignificant.

* I can live with losing the half-neighbor lists. It does however give a significant performance boosts for the kind of potentials that can use it. For a potential like EMT, I see almost a factor of 2 when there is only one element present, but still an improvement of 30-40% where there are two or more elements present, since a part of the calculations can be reused. So you may consider keeping the half lists.

* The NEIGH_PURE list type is in many ways the simplest. There may be some exotic boundary conditions that can only be simulated with NEIGH_RVEC, but frankly I doubt that it will ever be used. So I am all in favor of only keeping NEIGH_PURE. My experience is that if the model naturally would use one kind of neighbor lists, then it is not hard to code it to accept another kind instead, and the performance overhead is small.

* If you decide to keep both half and full lists, please write clearly in the documentation that a simulator is expected to support both of them, but a model should only support one of them. More can be expected from developers of simulators than from contributors of models - and only the model developer knows which type gives the best performance. Maybe even enforce that a model can only support one neighborlist mode :slight_smile:

* As for clear definitions of energy and force: You seem to propose to continue the current way of doing it, but just document it more clearly. I agree.

* You need to simplify one more thing: Memory management. That is currently (in my opinion) the most confusing part of the KIM API, and I am quite frankly not sure I have been doing it right in my own models and simulators. You can allocate memory with KIM_API_Allocate, but also directly with the KIM_API_set methods. Arrays can have dimensions, but it is not clear who should set them and when. It is often not even clear if an array should be allocated by the model or by the simulator. And what happens when the number of atoms change, e.g. due to migration in a parallel simulation? In version 2 of the API it should be painstakingly clear who is responsible for allocating memory, and when.

Who allocate what (and how, and in which order) when
- The model and simulator are initially paired.
- A calculation is done.
- A new calculation is done with a different number of atoms - or new elements. Note that this will happen all the time in parallel simulations as atoms migrate.
- The model is reinitialized with different parameters.
and a related question:
- If the model supports an optional quantity, and the simulator does not want it calculated, does it need to be allocated anyway? By who? It is probably best if it does get allocated, so the model can optimize stuff by calculating several things at once..

Maybe this is more a question of documentation than of documentation, although I suspect that the current API is a bit more complicated than it needs to be, also in this respect.

* What is your reason for having a particleStatus array instead of just having the contributing particles at the beginning of the arrays as in version 1? Not that I think it is a bad idea, I am just wondering why.

Finally, I would like to say that our own Atomic Simulation Environment has gone through a similar process. ASE version 1 supported a lot of stuff that we thought were a good ide, but which turned out to just bloat the interface. So in version 2 a lot was removed, and only a little was added.

Best regards - and good luck with the process.

Jakob

Dear Jakob,

Thanks for your thoughts. Below I'll try to address/reply to your comments.

* I can live with losing the half-neighbor lists. It does however give a significant performance boosts for the kind of potentials that can use it. For a potential like EMT, I see almost a factor of 2 when there is only one element present, but still an improvement of 30-40% where there are two or more elements present, since a part of the calculations can be reused. So you may consider keeping the half lists.

I'm not sure I understand why a factor of 2 is necessary. If a model that can work with a half list is only provide with a full list, can't it simply skip over those neighbors of atom i with index j > i and achieve most of the speed up of a half list? That is, is it not true that something like:

for (i=0; i < numberOfParticles; ++i) {
   for (j=0; j < numberOfNeighbors; ++j) {
     if (id(j) >= i) continue;
     // process i,id(j) as if a half list...
   }
}

will perform nearly as fast as a true half-list implementation?

* You need to simplify one more thing: Memory management. That is currently (in my opinion) the most confusing part of the KIM API, and I am quite frankly not sure I have been doing it right in my own models and simulators. You can allocate memory with KIM_API_Allocate, but also directly with the KIM_API_set methods. Arrays can have dimensions, but it is not clear who should set them and when. It is often not even clear if an array should be allocated by the model or by the simulator. And what happens when the number of atoms change, e.g. due to migration in a parallel simulation? In version 2 of the API it should be painstakingly clear who is responsible for allocating memory, and when.

Who allocate what (and how, and in which order) when
- The model and simulator are initially paired.
- A calculation is done.
- A new calculation is done with a different number of atoms - or new elements. Note that this will happen all the time in parallel simulations as atoms migrate.
- The model is reinitialized with different parameters.
and a related question:
- If the model supports an optional quantity, and the simulator does not want it calculated, does it need to be allocated anyway? By who? It is probably best if it does get allocated, so the model can optimize stuff by calculating several things at once..

Maybe this is more a question of documentation than of documentation, although I suspect that the current API is a bit more complicated than it needs to be, also in this respect.

Agreed. We'll consider some options for this and get back to the group with suggestions for how this can be done. We'll be very interested in your (and everyone's) opinion when we have some ideas.

* What is your reason for having a particleStatus array instead of just having the contributing particles at the beginning of the arrays as in version 1? Not that I think it is a bad idea, I am just wondering why.

This is a good question and we had considered the alternative approach that you suggest. (Actually, in version 1, only the half-list case uses the convention that the contributing particles are at the beginning of the list; full lists can have an arbitrary order, with non-contributing particles being identified as ones with zero neighbors.) We chose to use an explicit array indicating the status of each particle for the following reasons:

* The simulator does not need to renumber the particles for the purpose of identifying which ones are contributing/non-contributing. This provides a separation of information that is more flexible. This also allows the simulator to easily change the status of individual particles without the overhead of renumbering.

* This approach provides for possible future extensions to the set of possible status values. For v2, we are proposing only two valid status values: contributing and non-contributing. However, we could envision scenarios where we might decide to introduce other statuses. (One example, would be a "ignored" status that would tell the model to behave as if the ignored particle simple didn't exist. This would be useful, for example, for implementing LAMMPS's hybrid behavior, where different models are used to define different interactions between particles.)

I admit that, in a sense, the explicit array of statuses is more complex than just having a single "numberOfContributingParticles" value. So, I believe there are valid arguments in favor of either approach. I'm quite willing to have a back-and-forth discussion with the community about this issue. If the consensus prefers to have the contributing particles at the beginning of the particle list, we'll be happy to adopt that convention.

Cheers,

Ryan

Dear Ryan,

I like the simplifications that you are proposing. If I am not mistaken, it seems that most MD codes that are able to deal with reactive potentials (that are more complicated than EAM) use something like NEIGH-PURE-F internally anyway.

Some thoughts on particleStatus below:

  • What is your reason for having a particleStatus array instead of just
    having the contributing particles at the beginning of the arrays as in
    version 1? Not that I think it is a bad idea, I am just wondering why.

This is a good question and we had considered the alternative approach that you
suggest. (Actually, in version 1, only the half-list case uses the convention
that the contributing particles are at the beginning of the list; full lists
can have an arbitrary order, with non-contributing particles being identified
as ones with zero neighbors.) We chose to use an explicit array indicating the
status of each particle for the following reasons:

  • The simulator does not need to renumber the particles for the purpose of
    identifying which ones are contributing/non-contributing. This provides a
    separation of information that is more flexible. This also allows the
    simulator to easily change the status of individual particles without the
    overhead of renumbering.

  • This approach provides for possible future extensions to the set of possible
    status values. For v2, we are proposing only two valid status values:
    contributing and non-contributing. However, we could envision scenarios where
    we might decide to introduce other statuses. (One example, would be a “ignored”
    status that would tell the model to behave as if the ignored particle simple
    didn’t exist. This would be useful, for example, for implementing LAMMPS’s
    hybrid behavior, where different models are used to define different
    interactions between particles.)

I admit that, in a sense, the explicit array of statuses is more complex than
just having a single “numberOfContributingParticles” value. So, I believe
there are valid arguments in favor of either approach. I’m quite willing to
have a back-and-forth discussion with the community about this issue. If the
consensus prefers to have the contributing particles at the beginning of the
particle list, we’ll be happy to adopt that convention.

I am strongly in favor of what Ryan has suggested, i.e. a particleStatus array, from personal experience: We have recently implemented a scheme where we replace part of an atomic system by its harmonic response (similar to what quasicontinuum does), and here we needed to selectively disable energy contributions from atoms that are treated in the harmonic approximation to avoid double counting. This scheme would be easy to implement on top of the particleStatus array but would require additional bookkeeping if we needed to reorder all atoms. (Indeed, we ended up adding something like particleStatus to our own potentials - those would be super easy to move to KIM if this change is made.)

If the only application is domain decomposition, then just reordering the list may be faster and more memory efficient, but particleStatus certainly gives more flexibility with regard to possible future extensions, as Ryan already pointed out.

All the best,
Lars