kim-api  2.2.1+v2.2.1.GNU.GNU.
An Application Programming Interface (API) for the Knowledgebase of Interatomic Models (KIM).
ex_test_Ar_fcc_cluster_fortran.f90
Go to the documentation of this file.
1 !
2 ! CDDL HEADER START
3 !
4 ! The contents of this file are subject to the terms of the Common Development
5 ! and Distribution License Version 1.0 (the "License").
6 !
7 ! You can obtain a copy of the license at
8 ! http://www.opensource.org/licenses/CDDL-1.0. See the License for the
9 ! specific language governing permissions and limitations under the License.
10 !
11 ! When distributing Covered Code, include this CDDL HEADER in each file and
12 ! include the License file in a prominent location with the name LICENSE.CDDL.
13 ! If applicable, add the following below this CDDL HEADER, with the fields
14 ! enclosed by brackets "[]" replaced with your own identifying information:
15 !
16 ! Portions Copyright (c) [yyyy] [name of copyright owner]. All rights reserved.
17 !
18 ! CDDL HEADER END
19 !
20 
21 !
22 ! Copyright (c) 2013--2020, Regents of the University of Minnesota.
23 ! All rights reserved.
24 !
25 ! Contributors:
26 ! Ellad B. Tadmor
27 ! Ryan S. Elliott
28 ! Stephen M. Whalen
29 !
30 
31 module error
32  use, intrinsic :: iso_c_binding
33  implicit none
34 
35  public
36 
37 contains
38  recursive subroutine my_error(message)
39  implicit none
40  character(len=*, kind=c_char), intent(in) :: message
41 
42  print *, "* Error : ", trim(message)
43  stop 1
44  end subroutine my_error
45 
46  recursive subroutine my_warning(message)
47  implicit none
48  character(len=*, kind=c_char), intent(in) :: message
49 
50  print *, "* Warning : ", trim(message)
51  end subroutine my_warning
52 end module error
53 
54 !-------------------------------------------------------------------------------
55 !
56 ! module mod_neighborlist :
57 !
58 ! Module contains type and routines related to neighbor list calculation
59 !
60 !-------------------------------------------------------------------------------
61 
63 
64  use, intrinsic :: iso_c_binding
65 
66  public get_neigh
67 
68  type, bind(c) :: neighobject_type
69  real(c_double) :: cutoff
70  integer(c_int) :: number_of_particles
71  type(c_ptr) :: neighbor_list_pointer
72  end type neighobject_type
73 contains
74 
75  !-----------------------------------------------------------------------------
76  !
77  ! get_neigh neighbor list access function
78  !
79  ! This function implements Locator and Iterator mode
80  !
81  !-----------------------------------------------------------------------------
82  recursive subroutine get_neigh(data_object, number_of_neighbor_lists, &
83  cutoffs, neighbor_list_index, request, &
84  numnei, pnei1part, ierr) bind(c)
85  use error
86  implicit none
87 
88  !-- Transferred variables
89  type(c_ptr), value, intent(in) :: data_object
90  integer(c_int), value, intent(in) :: number_of_neighbor_lists
91  real(c_double), intent(in) :: cutoffs(*)
92  integer(c_int), value, intent(in) :: neighbor_list_index
93  integer(c_int), value, intent(in) :: request
94  integer(c_int), intent(out) :: numnei
95  type(c_ptr), intent(out) :: pnei1part
96  integer(c_int), intent(out) :: ierr
97 
98  !-- Local variables
99  integer(c_int) numberOfParticles
100  type(neighobject_type), pointer :: neighObject
101  integer(c_int), pointer :: neighborList(:, :)
102 
103  call c_f_pointer(data_object, neighobject)
104  numberofparticles = neighobject%number_of_particles
105  call c_f_pointer(neighobject%neighbor_list_pointer, neighborlist, &
106  [numberofparticles + 1, numberofparticles])
107 
108  if (number_of_neighbor_lists /= 1) then
109  call my_warning("invalid number of cutoffs")
110  ierr = 1
111  return
112  end if
113 
114  if (cutoffs(1) > neighobject%cutoff) then
115  call my_warning("neighbor list cutoff too small for model cutoff")
116  ierr = 1
117  return
118  end if
119 
120  if (neighbor_list_index /= 1) then
121  call my_warning("wrong list index")
122  ierr = 1
123  return
124  end if
125 
126  if ((request > numberofparticles) .or. (request < 1)) then
127  print *, request
128  call my_warning("Invalid part ID in get_neigh")
129  ierr = 1
130  return
131  end if
132 
133  ! set the returned number of neighbors for the returned part
134  numnei = neighborlist(1, request)
135 
136  ! set the location for the returned neighbor list
137  pnei1part = c_loc(neighborlist(2, request))
138 
139  ierr = 0
140  return
141  end subroutine get_neigh
142 
143 end module mod_neighborlist
144 
146  implicit none
147  public
148 
149 contains
150 
151  !-----------------------------------------------------------------------------
152  !
153  ! NEIGH_PURE_cluster_neighborlist
154  !
155  !-----------------------------------------------------------------------------
156  recursive subroutine neigh_pure_cluster_neighborlist( &
157  half, numberOfParticles, coords, cutoff, neighObject)
158  use, intrinsic :: iso_c_binding
159  use mod_neighborlist
160  implicit none
161 
162  !-- Transferred variables
163  logical, intent(in) :: half
164  integer(c_int), intent(in) :: numberOfParticles
165  real(c_double), dimension(3, numberOfParticles), &
166  intent(in) :: coords
167  real(c_double), intent(in) :: cutoff
168  type(neighobject_type), intent(inout) :: neighObject
169 
170  !-- Local variables
171  integer(c_int) i, j, a
172  real(c_double) dx(3)
173  real(c_double) r2
174  real(c_double) cutoff2
175  integer(c_int), pointer :: neighborList(:, :)
176 
177  call c_f_pointer(neighobject%neighbor_list_pointer, neighborlist, &
178  [numberofparticles + 1, numberofparticles])
179 
180  neighobject%cutoff = cutoff
181 
182  cutoff2 = cutoff**2
183 
184  do i = 1, numberofparticles
185  a = 1
186  do j = 1, numberofparticles
187  dx(:) = coords(:, j) - coords(:, i)
188  r2 = dot_product(dx, dx)
189  if (r2 <= cutoff2) then
190  ! part j is a neighbor of part i
191  if ((j > i) .OR. ((.not. half) .AND. (i /= j))) then
192  a = a + 1
193  neighborlist(a, i) = j
194  end if
195  end if
196  end do
197  ! part i has a-1 neighbors
198  neighborlist(1, i) = a - 1
199  end do
200 
201  return
202 
203  end subroutine neigh_pure_cluster_neighborlist
204 
205  !-----------------------------------------------------------------------------
206  !
207  ! create_FCC_configuration subroutine
208  !
209  ! creates a cubic configuration of FCC particles with lattice spacing
210  ! `FCCspacing' and `nCellsPerSide' cells along each direction.
211  !
212  ! With periodic==.true. this will result in a total number of particles equal
213  ! to 4*(nCellsPerSide)**3 + 6*(nCellsPerSide)**2 + 3*(nCellsPerSide) + 1
214  !
215  ! With periodic==.false. this will result in a total number of particles equal
216  ! to 4*(nCellsPerSide)**3
217  !
218  ! Returns the Id of the particle situated in the middle of the configuration
219  ! (this particle will have the most neighbors.)
220  !
221  !-----------------------------------------------------------------------------
222  recursive subroutine create_fcc_configuration(FCCspacing, nCellsPerSide, &
223  periodic, coords, MiddlePartId)
224  use, intrinsic :: iso_c_binding
225  implicit none
226  integer(c_int), parameter :: cd = c_double ! used for literal constants
227 
228  !-- Transferred variables
229  real(c_double), intent(in) :: FCCspacing
230  integer(c_int), intent(in) :: nCellsPerSide
231  logical, intent(in) :: periodic
232  real(c_double), intent(out) :: coords(3, *)
233  integer(c_int), intent(out) :: MiddlePartId
234  !
235  ! cluster setup variables
236  !
237  real(c_double) FCCshifts(3, 4)
238  real(c_double) latVec(3)
239  integer(c_int) a, i, j, k, m
240 
241  ! Create a cubic FCC cluster
242  !
243  fccshifts(1, 1) = 0.0_cd
244  fccshifts(2, 1) = 0.0_cd
245  fccshifts(3, 1) = 0.0_cd
246  fccshifts(1, 2) = 0.5_cd * fccspacing
247  fccshifts(2, 2) = 0.5_cd * fccspacing
248  fccshifts(3, 2) = 0.0_cd
249  fccshifts(1, 3) = 0.5_cd * fccspacing
250  fccshifts(2, 3) = 0.0_cd
251  fccshifts(3, 3) = 0.5_cd * fccspacing
252  fccshifts(1, 4) = 0.0_cd
253  fccshifts(2, 4) = 0.5_cd * fccspacing
254  fccshifts(3, 4) = 0.5_cd * fccspacing
255 
256  middlepartid = 1 ! Always put middle particle as #1
257  a = 1 ! leave space for middle particle as particle #1
258  do i = 1, ncellsperside
259  latvec(1) = (i - 1) * fccspacing
260  do j = 1, ncellsperside
261  latvec(2) = (j - 1) * fccspacing
262  do k = 1, ncellsperside
263  latvec(3) = (k - 1) * fccspacing
264  do m = 1, 4
265  a = a + 1
266  coords(:, a) = latvec + fccshifts(:, m)
267  if ((i == ncellsperside / 2 + 1) &
268  .and. (j == ncellsperside / 2 + 1) &
269  .and. (k == ncellsperside / 2 + 1) &
270  .and. (m == 1)) &
271  then
272  ! put middle particle as #1
273  coords(:, 1) = latvec + fccshifts(:, m)
274  a = a - 1
275  end if
276  end do
277  end do
278  if (.not. periodic) then
279  ! Add in the remaining three faces
280  ! pos-x face
281  latvec(1) = ncellsperside * fccspacing
282  latvec(2) = (i - 1) * fccspacing
283  latvec(3) = (j - 1) * fccspacing
284  a = a + 1; coords(:, a) = latvec
285  a = a + 1; coords(:, a) = latvec + fccshifts(:, 4)
286  ! pos-y face
287  latvec(1) = (i - 1) * fccspacing
288  latvec(2) = ncellsperside * fccspacing
289  latvec(3) = (j - 1) * fccspacing
290  a = a + 1; coords(:, a) = latvec
291  a = a + 1; coords(:, a) = latvec + fccshifts(:, 3)
292  ! pos-z face
293  latvec(1) = (i - 1) * fccspacing
294  latvec(2) = (j - 1) * fccspacing
295  latvec(3) = ncellsperside * fccspacing
296  a = a + 1; coords(:, a) = latvec
297  a = a + 1; coords(:, a) = latvec + fccshifts(:, 2)
298  end if
299  end do
300  if (.not. periodic) then
301  ! Add in the remaining three edges
302  latvec(1) = (i - 1) * fccspacing
303  latvec(2) = ncellsperside * fccspacing
304  latvec(3) = ncellsperside * fccspacing
305  a = a + 1; coords(:, a) = latvec
306  latvec(1) = ncellsperside * fccspacing
307  latvec(2) = (i - 1) * fccspacing
308  latvec(3) = ncellsperside * fccspacing
309  a = a + 1; coords(:, a) = latvec
310  latvec(1) = ncellsperside * fccspacing
311  latvec(2) = ncellsperside * fccspacing
312  latvec(3) = (i - 1) * fccspacing
313  a = a + 1; coords(:, a) = latvec
314  end if
315  end do
316  if (.not. periodic) then
317  ! Add in the remaining corner
318  a = a + 1; coords(:, a) = ncellsperside * fccspacing
319  end if
320 
321  return
322 
323  end subroutine create_fcc_configuration
324 
325 end module mod_utility
326 
327 !*******************************************************************************
328 !**
329 !** PROGRAM vc_forces_numer_deriv
330 !**
331 !** KIM compliant program to perform numerical derivative check on a model
332 !**
333 !*******************************************************************************
334 
335 !-------------------------------------------------------------------------------
336 !
337 ! Main program
338 !
339 !-------------------------------------------------------------------------------
341  use, intrinsic :: iso_c_binding
342  use error
345  use mod_neighborlist
346  use mod_utility
347  implicit none
348  integer(c_int), parameter :: cd = c_double ! used for literal constants
349 
350  integer(c_int), parameter :: nCellsPerSide = 2
351  integer(c_int), parameter :: DIM = 3
352 
353  real(c_double), parameter :: cutpad = 0.75_cd
354  real(c_double), parameter :: FCCspacing = 5.260_cd
355  real(c_double), parameter :: min_spacing = 0.8 * fccspacing
356  real(c_double), parameter :: max_spacing = 1.2 * fccspacing
357  real(c_double), parameter :: spacing_incr = 0.025 * fccspacing
358  real(c_double) :: current_spacing
359  real(c_double) :: force_norm
360 
361  character(len=256, kind=c_char) :: modelname
362 
363  integer(c_int), parameter :: N = 4 * (ncellsperside)**3 &
364  + 6 * (ncellsperside)**2 &
365  + 3 * (ncellsperside) + 1
366 
367  type(neighobject_type), target :: neighObject
368  integer(c_int), allocatable, target :: neighborList(:, :)
369 
370  type(kim_model_handle_type) :: model_handle
371  type(kim_compute_arguments_handle_type) :: compute_arguments_handle
372  real(c_double) :: influence_distance
373  integer(c_int) :: number_of_neighbor_lists
374  real(c_double) :: cutoff
375  real(c_double) :: cutoffs(1)
376  integer(c_int) :: &
377  model_will_not_request_neighbors_of_noncontributing_particles(1)
378  integer(c_int), target :: particle_species_codes(n)
379  integer(c_int), target :: particle_contributing(n)
380  real(c_double), target :: energy
381  real(c_double), target :: coords(dim, n)
382  real(c_double), target :: forces(dim, n)
383  integer(c_int) i, j, ierr, ierr2
384 
385  integer(c_int) species_is_supported
386  integer(c_int) species_code
387  integer(c_int) requested_units_accepted
388  integer(c_int) number_of_model_routine_names
389  type(kim_model_routine_name_type) model_routine_name
390  integer(c_int) present
391  integer(c_int) required
392  type(kim_supported_extensions_type), target :: supported_extensions
393  character(len=KIM_MAX_EXTENSION_ID_LENGTH, kind=c_char) :: id_string
394 
395  integer :: middledum
396 
397  ! Initialize
398  ierr = 0
399  supported_extensions%number_of_supported_extensions = 0
400 
401  ! Get KIM Model name to use
402  print '("Please enter a valid KIM model name: ")'
403  read (*, *) modelname
404 
405  ! Create empty KIM object
406  !
407  call kim_model_create(kim_numbering_one_based, &
408  kim_length_unit_a, &
409  kim_energy_unit_ev, &
410  kim_charge_unit_e, &
411  kim_temperature_unit_k, &
412  kim_time_unit_ps, &
413  trim(modelname), &
414  requested_units_accepted, &
415  model_handle, ierr)
416  if (ierr /= 0) then
417  call my_error("kim_api_create")
418  end if
419 
420  ! check that we are compatible
421  if (requested_units_accepted == 0) then
422  call my_error("Must adapt to model units")
423  end if
424 
425  ! check that we know about all required routines
426  call kim_get_number_of_model_routine_names(number_of_model_routine_names)
427  do i = 1, number_of_model_routine_names
428  call kim_get_model_routine_name(i, model_routine_name, ierr)
429  if (ierr /= 0) then
430  call my_error("kim_get_model_routine_name")
431  end if
432  call kim_is_routine_present(model_handle, model_routine_name, present, &
433  required, ierr)
434  if (ierr /= 0) then
435  call my_error("kim_is_routine_present")
436  end if
437 
438  if ((present == 1) .and. (required == 1)) then
439  if (.not. ((model_routine_name == kim_model_routine_name_create) &
440  .or. (model_routine_name == &
441  kim_model_routine_name_compute_arguments_create) &
442  .or. (model_routine_name == kim_model_routine_name_compute) &
443  .or. (model_routine_name == kim_model_routine_name_refresh) &
444  .or. (model_routine_name == &
445  kim_model_routine_name_compute_arguments_destroy) &
446  .or. (model_routine_name == kim_model_routine_name_destroy))) &
447  then
448  call my_error("Unknown required ModelRoutineName found.")
449  end if
450  end if
451  end do
452 
453  ! check that model supports Ar
454  !
455  call kim_get_species_support_and_code( &
456  model_handle, kim_species_name_ar, species_is_supported, species_code, ierr)
457  if ((ierr /= 0) .or. (species_is_supported /= 1)) then
458  call my_error("Model does not support Ar")
459  end if
460 
461  ! Check supported extensions, if any
462  call kim_is_routine_present( &
463  model_handle, kim_model_routine_name_extension, present, required, ierr)
464  if (ierr /= 0) then
465  call my_error("Unable to get Extension present/required.")
466  end if
467  if (present /= 0) then
468  call kim_extension(model_handle, kim_supported_extensions_id, &
469  c_loc(supported_extensions), ierr)
470  if (ierr /= 0) then
471  call my_error("Error returned from kim_model_extension().")
472  end if
473  write (*, '(A,I2,A)') "Model Supports ", &
474  supported_extensions%number_of_supported_extensions, &
475  " Extensions:"
476  do i = 1, supported_extensions%number_of_supported_extensions
477  call kim_c_char_array_to_string( &
478  supported_extensions%supported_extension_id(:, i), id_string)
479  write (*, '(A,I2,A,A,A,A,I2)') " supportedExtensionID[", i, '] = "', &
480  trim(id_string), '" ', &
481  "which has required = ", &
482  supported_extensions%supported_extension_required(i)
483  end do
484  end if
485 
486  ! Best-practice is to check that the model is compatible
487  ! but we will skip it here
488 
489  ! create compute_arguments object
490  call kim_compute_arguments_create( &
491  model_handle, compute_arguments_handle, ierr)
492  if (ierr /= 0) then
493  call my_error("kim_model_compute_arguments_create")
494  end if
495 
496  ! register memory with the KIM system
497  ierr = 0
498  call kim_set_argument_pointer( &
499  compute_arguments_handle, kim_compute_argument_name_number_of_particles, &
500  n, ierr2)
501  ierr = ierr + ierr2
502  call kim_set_argument_pointer( &
503  compute_arguments_handle, &
504  kim_compute_argument_name_particle_species_codes, &
505  particle_species_codes, ierr2)
506  ierr = ierr + ierr2
507  call kim_set_argument_pointer( &
508  compute_arguments_handle, &
509  kim_compute_argument_name_particle_contributing, particle_contributing, &
510  ierr2)
511  ierr = ierr + ierr2
512  call kim_set_argument_pointer( &
513  compute_arguments_handle, kim_compute_argument_name_coordinates, coords, &
514  ierr2)
515  ierr = ierr + ierr2
516  call kim_set_argument_pointer( &
517  compute_arguments_handle, kim_compute_argument_name_partial_energy, &
518  energy, ierr2)
519  ierr = ierr + ierr2
520  call kim_set_argument_pointer( &
521  compute_arguments_handle, kim_compute_argument_name_partial_forces, &
522  forces, ierr2)
523  ierr = ierr + ierr2
524  if (ierr /= 0) then
525  call my_error("set_argument_pointer")
526  end if
527 
528  ! Allocate storage for neighbor lists
529  !
530  allocate (neighborlist(n + 1, n))
531  neighobject%neighbor_list_pointer = c_loc(neighborlist)
532  neighobject%number_of_particles = n
533 
534  ! Set pointer in KIM object to neighbor list routine and object
535  !
536  call kim_set_callback_pointer( &
537  compute_arguments_handle, kim_compute_callback_name_get_neighbor_list, &
538  kim_language_name_fortran, c_funloc(get_neigh), c_loc(neighobject), ierr)
539  if (ierr /= 0) then
540  call my_error("set_callback_pointer")
541  end if
542 
543  call kim_get_influence_distance(model_handle, influence_distance)
544  call kim_get_number_of_neighbor_lists(model_handle, &
545  number_of_neighbor_lists)
546  if (number_of_neighbor_lists /= 1) then
547  call my_error("too many neighbor lists")
548  end if
549  call kim_get_neighbor_list_values( &
550  model_handle, cutoffs, &
551  model_will_not_request_neighbors_of_noncontributing_particles, ierr)
552  if (ierr /= 0) then
553  call my_error("get_neighbor_list_values")
554  end if
555  cutoff = cutoffs(1)
556 
557  ! Setup cluster
558  !
559  do i = 1, n
560  particle_species_codes(i) = species_code
561  end do
562 
563  ! setup contributing particles
564  do i = 1, n
565  particle_contributing(i) = 1 ! every particle contributes
566  end do
567 
568  ! Print output header
569  !
570  print *
571  print *, 'This is Test : ex_test_Ar_fcc_cluster_fortran'
572  print *
573  print '(80(''-''))'
574  print '("Results for KIM Model : ",A)', trim(modelname)
575 
576  ! print header
577  print '(3A20)', "Energy", "Force Norm", "Lattice Spacing"
578  ! do the computations
579  current_spacing = min_spacing
580  do while (current_spacing < max_spacing)
581 
582  call create_fcc_configuration(current_spacing, ncellsperside, .false., &
583  coords, middledum)
584  ! Compute neighbor lists
585  call neigh_pure_cluster_neighborlist(.false., n, coords, &
586  (cutoff + cutpad), neighobject)
587 
588  ! Call model compute
589  call kim_compute(model_handle, compute_arguments_handle, ierr)
590  if (ierr /= 0) then
591  call my_error("kim_api_model_compute")
592  end if
593 
594  ! compue force_norm
595  force_norm = 0.0
596  do i = 1, n
597  do j = 1, dim
598  force_norm = force_norm + forces(j, i) * forces(j, i)
599  end do
600  end do
601  force_norm = sqrt(force_norm)
602 
603  ! Print results to screen
604  !
605  print '(3ES20.10)', energy, force_norm, current_spacing
606 
607  current_spacing = current_spacing + spacing_incr
608  end do
609 
610  ! Deallocate neighbor list object
611  deallocate (neighborlist)
612 
613  call kim_compute_arguments_destroy( &
614  model_handle, compute_arguments_handle, ierr)
615  if (ierr /= 0) then
616  call my_error("compute_arguments_destroy")
617  end if
618  call kim_model_destroy(model_handle)
619 
recursive subroutine, public get_neigh(data_object, number_of_neighbor_lists, cutoffs, neighbor_list_index, request, numnei, pnei1part, ierr)
recursive subroutine my_warning(message)
recursive subroutine neigh_pure_cluster_neighborlist(half, numberOfParticles, coords, cutoff, neighObject)
recursive subroutine my_error(message)
recursive subroutine create_fcc_configuration(FCCspacing, nCellsPerSide, periodic, coords, MiddlePartId)
character(len= *, kind=c_char), parameter, public kim_supported_extensions_id
program ex_test_ar_fcc_cluster_fortran
The only standard extension defined by the KIM API.