Minimal model and driver template files for kim-api-v2

Hello,

Attached are 6 code files (two each for C++, C, and Fortran, respectively) that can be used as a starting point for writing models and model drivers for kim-api-v2.

Look for comments starting with '@@' for places where additional code can/should/must be added.

Cheers,

Ryan

ModelCpp.cpp (4.13 KB)

ModelDriverCpp.cpp (4.27 KB)

model_fortran.F90 (4.86 KB)

ModelC.c (4.83 KB)

ModelDriverC.c (4.95 KB)

model_driver_fortran.F90 (4.95 KB)

Hi Ryan,

This is great and very helpful! Thanks! I came across a couple of questions: The two scripts “model_fortran.f90” and “model_driver_fortran.f90” are exactly the same, with identical subroutines. Do I need to repeat the same codes in both files?

I guess I have not understood it clearly. Let’s assume that I want to code up a \emph{toy} potential, where the total potential energy of the system is E=\sum_{i}^{all atoms in the system}{a * \sum_{j}^{neighbors of atom i within the cutoff distance}{1}}, where a is a hyperparameter assigned by the user. This is simply the particle energy is a hyperparameter times the number of neighbors. Then:

1- Where and how should I read these two parameters from the “.params” text file?
2- I should code up the energy in compute_energy_forces subroutine in both model_fortran.f90 and model_driver_fortran.f90 scripts, right?
3- What are create, destroy, and refresh subroutines? Do I need to code them also?

Thanks and regards,
Alireza

Updated versions of fortran templates are atteached.

model_fortran.F90 (5.17 KB)

model_driver_fortran.F90 (5.26 KB)

Hi Alireza,

Hi Ryan,

This is great and very helpful! Thanks! I came across a couple of
questions: The two scripts "model_fortran.f90" and
"model_driver_fortran.f90" are exactly the same, with identical
subroutines. Do I need to repeat the same codes in both files?

Good question. No. The model_fortran.f90 file is a template for a "stand-alone" model (one that implements a single model with its parameters for a specific material; That is, a model that does NOT use a model driver.)

The model_driver_fortran.f90 file is a template for a model driver which implements the functional form of the model, but must be combined with a "parameterized model" (which just consists essentially of parameter file(s) that provide the parameters for a specific material).

So, No you don't need to implement/repeat the codes in two files. I think in your case you will want to implement a model driver code and then also create the parameterized models that provide the parameters to the model driver.

I guess I have not understood it clearly. Let's assume that I want to code
up a \emph{toy} potential, where the total potential energy of the system
is E=\\sum\_\{i\}^\{all atoms in the system\}\{a \* \\sum\_\{j\}^\{neighbors of atom i within the cutoff distance\}\{1\}\}, where a is a hyperparameter assigned by
the user. This is simply the particle energy is a hyperparameter times the
number of neighbors. Then:

1- Where and how should I read these two parameters from the ".params" text
file?

The params file would be part of the "parameterized model". You can see an example of how this is set up (you need the parameter file, and a Makefile) here

kim-api-v2*/examples/models/ex_model_Ar_P_LJ

The parameter files can have any format you want. The model driver code completely defines the expected format.

These files will be read by the model driver code during the execution of its model_create() routine.

2- I should code up the energy in compute_energy_forces
<https://openkim.org/kim-api/docs/namespaceex__model__driver__p__lj.html#ac4ee74244a51d7808e37a42e576bb3bf>
subroutine in both model_fortran.f90 and model_driver_fortran.f90 scripts,
right?

The energy computation would be coded in the "model_compute()" routine in just the model_driver_fortran.F90 template.

3- What are create, destroy, and refresh subroutines? Do I need to code
them also?

There are 6 routines that go into the full implementation of a model (or driver). These are described briefly here:

https://openkim.org/kim-api/docs/implementation.html

I like your example. I've coded it up to help you get the up to speed. Attached.

Ryan

toy_model_driver.txz (3.51 KB)

toy_parameterized_model.txz (1.08 KB)

Hi Ryan,

Thanks for the toy model and model driver. These are great as starting point for model (driver) development. I continued this discussion in the other thread, to keep this thread clean for future developers.

Thanks again and regards,
Alireza

Hi Ryan,

These toy model and model driver examples are great! I was wondering though if you could also post a simple test example for the toy model driver. Specifically, I am not sure how I should plug the “toy.params” file into the model driver?

Also, what is the difference between “User Collection” and “System Collection” under kim-api-v2-collections-management list?

Thanks and regards,
Alireza

Hi Alireza,

You install both the toy_model_driver and the toy_paramaterized_model using the kim-api-v2-collections-management utility and then you can just use the toy_model in your simulations. When you use the toy_parameterized_model the kim-api takes care of passing the toy.params file to the toy_model_driver for you. If you make a change to toy.params, just reinstall the toy_parameterized_model. No need to reinstall the toy_model_driver (and vice-versa).

The "system-collection" is a set of models and drivers stored "with the kim-api" library installation. (often this is in the systems folders /usr or /usr/local, but it doesn't have to be; Users without root premissions would have to install the library within their home directory.) The "user-collection" is a set of models and drivers stored in a location controled by the user who owns the simulator process that is using the kim-api. These locations are set in a configuration file. By default, the configuration file is ~/.kim-api/config-v2 and the user-collection models and drivers are stored in ~/.kim-api/v2_models and ~/.kim-api/v2_model_drivers, respectively.
There is no other difference between these collections. They are really just ways for different users to have different collections of models and drivers that the kim-api can easily locate and use.

If you run

$kim-api-v2-collections-management list you will see the directorys listed along with the list of currently installed models or drivers in that collection. For example, if I build and install the kim-api into my user account (using a "prefix" of "/Users/relliott/local") and the examples that come with it. Then I install the toy_* model and driver into the user collection (by issuing the following commands from the directory where the toys are located:$ kim-api-v2-collections-management install user ./toy_model_driver
Installing..... Model Driver... toy_model_driver.

Success!

$kim-api-v2-collections-management install user ./toy_parameterized_model Using installed driver... toy_model_driver Installing..... Model.......... toy_parameterized_model. Success! ), I would see this from the collections management utility:$ kim-api-v2-collections-management list

Knowledgebase of Interatomic Models (KIM) --- Model Collections Listing

Thanks Ryan! I want to constantly develop and test a new model driver, and I guess in this case it had better be under user-collection (and not system-collection), right?

Also, I am thinking of a minimal test script that constructs a system of two atoms of Ar, and then call the toy_parametrized_model to calculate just the potential energy of the system (no forces for now). I can do it with Lammps and kim pair_style, but is there any way to do it independent of Lammps?

Regards,
Alireza

Thanks Ryan! I want to constantly develop and test a new model driver, and
I guess in this case it had better be under user-collection (and not
system-collection), right?

That is probably a good idea. It will allow you to avoid the use of "sudo".

Also, I am thinking of a minimal test script that constructs a system of
two atoms of Ar, and then call the toy_parametrized_model to calculate just
the potential energy of the system (no forces for now). I can do it with
Lammps and kim pair_style, but is there any way to do it independent of
Lammps?

Yes. This is easy. You can start with the examples in the kim-api package. I've attached a toy_dimer_simulator code that was a simple modification of the ex_test_Ar_fcc_cluster_fortran example. To use it:

cd toy\_dimer\_simulator make
\$ printf toy_parameterized_model | ./toy_dimer_simulator
Please enter a valid KIM model name:

This is Test : toy_dimer_simulator

toy_dimer_simulator.txz (3.21 KB)

This is very useful! Thanks Ryan!

Trying to dig into the “toy_model_driver.F90” code you attached this thread a few days ago, I am not sure I understood how you read parameters from “toy.params” script. Is it related to these two lines?

call kim_model_compute_get_model_buffer_pointer(model_compute_handle, pbuf)

call c_f_pointer(pbuf, buf)

But then how it knows that the first line in toy.params is species, the second cutoff, and the third hyperparameter?

Regards,
Alireza

Hi Alireza,

No, the file is read starting on line 333 of toy_model_driver.F90 where it says:

! Read in model parameters from parameter file
!
call kim_model_driver_create_get_parameter_file_name( &
model_driver_create_handle, 1, parameter_file_name, ierr)
if (ierr /= 0) then
kim_log_message = "Unable to get parameter file name"
LOG_ERROR()
return
end if
open(10,file=parameter_file_name,status="old")
read(10,*,iostat=ierr,err=100) buffer%cutoff(1) ! in A
read(10,*,iostat=ierr,err=100) buffer%hyperparameter ! in eV
close(10)

Ryan

Thanks Ryan! Sorry, I will ask you questions as I go through these sample models/model drivers. What is the difference between species_name and species_name_string?

character(len=8, kind=c_char) :: species_name_string

type(kim_species_name_type) :: species_name

Specifically, what is kim_species_name_type?

Regards,
Alireza

Hi Alireza,

species_name_string is a variable that will store the character string representing the atomic species ("H", "He", ...) that is read in from the parameter file.

species_name is a variable of type "kim_species_name_type". The kim_species_name_type is a data type that represents a particle's species. We use a data type to allow the compiler to tell you if you are using an invalid value at compile-time. The species_name variable will be set to the appropriate value using the kim_species_name_from_string() routine by passing it the species_name string:

call kim_species_name_from_string(trim(species_name_string), species_name)

The use of a data type is preferable to the use of strings for performance, for type-checking at compile-time, so that the compiler can catch typos too.

Ryan

Thanks Ryan! If I have a system of multiple species (say two species), then how should I register them?

Also, is there any simple way to get the list of atoms neighboring to atom A, their positions, and their chemical species?

Regards,
Alireza

Thanks Ryan! If I have a system of multiple species (say two species), then
how should I register them?

Just make multiple calls to the kim_model_driver_create_set_species_code() routine, one for each species.

Also, is there any simple way to get the list of atoms neighboring to atom
A, their positions, and their chemical species?

You can use the neighbor particle id's (obtained from the call to kim_model_compute_arguments_get_neighbor_list() routine and index into the coordinates, and particle_species arrays...

Ryan

Thanks Ryan! If I have a system of multiple species (say two species), then

how should I register them?

Just make multiple calls to the kim_model_driver_create_set_species_code()
routine, one for each species.

I did it this way:

do p = 1, buffer%num_species

call kim_species_name_from_string(trim(buffer%species_name_strings(p)),
species_names(p))

call kim_model_driver_create_set_species_code(model_driver_create_handle,
species_names(p), species_code, ierr)

end do

species_code is just a scalar parameter 1 apparently, so I am not sure what
the second call does. But I hope I have done it in the correct way!

Also, is there any simple way to get the list of atoms neighboring to atom

A, their positions, and their chemical species?

You can use the neighbor particle id's (obtained from the call to
kim_model_compute_arguments_get_neighbor_list() routine and index into
the coordinates, and particle_species arrays...

For particle species, I should do

call
kim_model_compute_arguments_get_argument_pointer(model_compute_arguments_handle,
kim_compute_argument_name_particle_species_codes, n,
particle_species_codes, ierr2)

right? But then what is particle_species_codes? Is it a list of length n
where each component of the list is the species code of the corresponding
particle? How is the species code decided? Like what is the code for Ar,
what is say for Pt, etc?

Ryan

--

--- You received this message because you are subscribed to the Google
Groups "openkim" group.
To unsubscribe from this group and stop receiving emails from it, send an
email to [email protected].
For more options, visit https://groups.google.com/d/optout.

Regards,
Alireza

Thanks Ryan! If I have a system of multiple species (say two species), then

how should I register them?

Just make multiple calls to the kim_model_driver_create_set_species_code()
routine, one for each species.

I did it this way:

do p = 1, buffer%num_species

call kim_species_name_from_string(trim(buffer%species_name_strings(p)),
species_names(p))

call kim_model_driver_create_set_species_code(model_driver_create_handle,
species_names(p), species_code, ierr)

end do

You'll need to change the species_code to a unique number for each species. See below.

species_code is just a scalar parameter 1 apparently, so I am not sure what
the second call does. But I hope I have done it in the correct way!

Also, is there any simple way to get the list of atoms neighboring to atom

A, their positions, and their chemical species?

You can use the neighbor particle id's (obtained from the call to
kim_model_compute_arguments_get_neighbor_list() routine and index into
the coordinates, and particle_species arrays...

For particle species, I should do

call
kim_model_compute_arguments_get_argument_pointer(model_compute_arguments_handle,
kim_compute_argument_name_particle_species_codes, n,
particle_species_codes, ierr2)

right? But then what is particle_species_codes? Is it a list of length n
where each component of the list is the species code of the corresponding
particle? How is the species code decided? Like what is the code for Ar,
what is say for Pt, etc?

Yes. The model defines these numbers: see above. These codes are how the simulator tells the models what species each particle is. They are internal codes used for efficieny (to avoid comparing strings all the time).

Ryan

Hi Ryan,

I just came back to this which was stopped. I am still confused with species_name_string, species_name, and species_code. As a background to you, neural network models are general and can be build for any species. So I have to read species_name_strings from a .param file provided by the user. These are the questions then:

1- Are species_name and species_code some parameters that are already defined by the developers of openkim, or these two variables are something \emph{I} should define it arbitrarily in my model?

2- What is the difference between species_name_string and species_name? If I should define these variables arbitrarily in my model, can I define them to be identical?

3- If I should define these variables in my model, then should I do a long look-up periodic table like:

if species_name_string = ‘H’ then:
species_code = 1
else if species_name_string = ‘He’ then:
species_code = 2

else if species_name_string = ‘Li’ then:
species_code = 3
etc.

4- If on the other hand, these variables are pre-defined by the developers of openkim, then how can I access them?

Thanks and regards,
Alireza

1- Are species_name and species_code some parameters that are already
defined by the developers of openkim, or these two variables are something
\emph{I} should define it arbitrarily in my model?

2- What is the difference between species_name_string and species_name? If
I should define these variables arbitrarily in my model, can I define them
to be identical?

There are defined constants for all of the periodic table species. See the kim-api-v2.../fortran/include/kim_species_name_module.f90 file.

These are the constants that the kim-api uses to represent each species.

If you read a string from a parameter file representing the atom species (such as "Ni"), then you can convert this to a type(kim_species_name_type) variable that is equal to the corresponding kim_species_name_type defined constant using the

kim_species_name_from_string()

function.

The species_code is an integer that your model driver source code must define to represent a particular species. The simulator will provide an array, the "particleSpecies" array, containing these integer codes to communicate the species of each particle in the simulation to your model driver.

You only need to define a code for the species that your (parameterized) model supports.

There is an example of all this in the LennardJones612__MD_414112407348_003 code (it is in C++). If you look starting at line 368 of LennardJones612Implementation.cpp, you will see that the model driver reads in lines from the parameter file corresponding to:

spec1, spec2, nextCutoff, nextEpsilon, nextSigma

spec1 and spec2 are strings such as "He", "Ni", etc.

Then, these are converted to KIM::SpeciesName variables on line 383. This is the C++ equivalent of making a call to the

kim_species_name_from_string()

function. Then it figures out the species code it will/is using for those species and registers it with the kim-api on line 395.

Cheers,

Ryan

Ryan,

How can I get the species_code from species_name_string or species_name after I assigned it with?

call kim_model_driver_create_set_species_code(model_driver_create_handle, species_name, species_code, ierr)

P.S. This is a frustrating slow way that I have to ask you these one by one. But I cannot think of an alternative way!

Regards,

Alireza