(git:98357aa)
Loading...
Searching...
No Matches
manybody_e3nn.F
Go to the documentation of this file.
1!--------------------------------------------------------------------------------------------------!
2! CP2K: A general program to perform molecular dynamics simulations !
3! Copyright 2000-2026 CP2K developers group <https://cp2k.org> !
4! !
5! SPDX-License-Identifier: GPL-2.0-or-later !
6!--------------------------------------------------------------------------------------------------!
7
8! **************************************************************************************************
9!> \brief Shared TorchScript evaluation path for e3nn-based equivariant message-passing
10!> potentials (NequIP, Allegro and MACE).
11!> \par History
12!> Implementation of NequIP and Allegro potentials - [gtocci] 2022
13!> Index mapping of atoms from .xyz to Allegro config.yaml file - [mbilichenko] 2024
14!> Refactoring and update to NequIP version >= v0.7.0 - [gtocci] 2026
15!> Renamed manybody_nequip -> manybody_e3nn as it now also serves MACE - [xysun] 2026
16!> \author Gabriele Tocci
17! **************************************************************************************************
19
21 USE cell_types, ONLY: cell_type
30 USE kinds, ONLY: default_string_length,&
31 dp,&
32 int_8
41 USE torch_api, ONLY: &
45#include "./base/base_uses.f90"
46
47 IMPLICIT NONE
48
49 PRIVATE
52
53 CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'manybody_e3nn'
54
55 TYPE, PRIVATE :: nequip_work_type
56 INTEGER :: target_pot_type
57 INTEGER :: n_atoms_use
58 LOGICAL :: use_virial
59
60 TYPE(cell_type), POINTER :: cell => null()
61 TYPE(pos_type), DIMENSION(:), POINTER :: r_pbc => null()
62 TYPE(distribution_1d_type), POINTER :: local_particles => null()
63 TYPE(particle_type), POINTER :: particle_set(:) => null()
64 TYPE(mp_para_env_type), POINTER :: para_env => null()
65
66 LOGICAL, ALLOCATABLE :: use_atom(:)
67 INTEGER(kind=int_8), ALLOCATABLE :: local_edges(:, :)
68 REAL(kind=dp), ALLOCATABLE :: local_shifts(:, :)
69 INTEGER(kind=int_8), ALLOCATABLE :: final_edges(:, :)
70 REAL(kind=dp), ALLOCATABLE :: final_shifts(:, :)
71 INTEGER, DIMENSION(:), ALLOCATABLE :: kind_mapper
72 LOGICAL, ALLOCATABLE :: sum_energy(:)
73 END TYPE nequip_work_type
74
75CONTAINS
76
77! **************************************************************************************************
78!> \brief ...
79!> \param nonbonded ...
80!> \param particle_set ...
81!> \param local_particles ...
82!> \param cell ...
83!> \param atomic_kind_set ...
84!> \param potparm ...
85!> \param r_last_update_pbc ...
86!> \param pot_total ...
87!> \param fist_nonbond_env ...
88!> \param para_env ...
89!> \param use_virial ...
90!> \param target_pot_type ...
91!> \par History
92!> Implementation of the nequip potential - [gtocci] 2022
93!> Refactoring and unifying NequIP and Allegro - [gtocci] 2026
94!> \author Gabriele Tocci - University of Zurich
95! **************************************************************************************************
96 SUBROUTINE e3nn_energy_store_force_virial(nonbonded, particle_set, local_particles, cell, &
97 atomic_kind_set, potparm, r_last_update_pbc, &
98 pot_total, fist_nonbond_env, para_env, use_virial, &
99 target_pot_type)
100
101 TYPE(fist_neighbor_type), POINTER :: nonbonded
102 TYPE(particle_type), POINTER :: particle_set(:)
103 TYPE(distribution_1d_type), POINTER :: local_particles
104 TYPE(cell_type), POINTER :: cell
105 TYPE(atomic_kind_type), POINTER :: atomic_kind_set(:)
106 TYPE(pair_potential_pp_type), POINTER :: potparm
107 TYPE(pos_type), DIMENSION(:), POINTER :: r_last_update_pbc
108 REAL(kind=dp) :: pot_total
109 TYPE(fist_nonbond_env_type), POINTER :: fist_nonbond_env
110 TYPE(mp_para_env_type), POINTER :: para_env
111 LOGICAL, INTENT(IN) :: use_virial
112 INTEGER, INTENT(IN) :: target_pot_type
113
114 CHARACTER(LEN=*), PARAMETER :: routinen = 'e3nn_energy_store_force_virial'
115
116 INTEGER :: handle
117 TYPE(nequip_data_type), POINTER :: neq_data
118 TYPE(nequip_pot_type), POINTER :: neq_pot
119 TYPE(nequip_work_type) :: nequip_work
120 TYPE(torch_dict_type) :: outputs
121
122 CALL timeset(routinen, handle)
123
124 CALL nequip_work_create(nequip_work, atomic_kind_set, particle_set, local_particles, cell, &
125 r_last_update_pbc, para_env, potparm, target_pot_type, use_virial, &
126 neq_pot)
127
128 IF (.NOT. ASSOCIATED(neq_pot)) THEN
129 CALL timestop(handle)
130 RETURN
131 END IF
132
133 CALL build_local_edges_shifts(nonbonded, potparm, nequip_work)
134
135 CALL build_torch_edge_indexes(nequip_work)
136
137 CALL setup_neq_data(fist_nonbond_env, neq_data, neq_pot, nequip_work)
138
139 IF (nequip_work%target_pot_type == nequip_type .OR. &
140 nequip_work%target_pot_type == mace_type) THEN
141 CALL prepare_edges_shifts_nequip(nequip_work)
142 ELSE
143 CALL prepare_edges_shifts_allegro(nequip_work)
144 END IF
145
146 CALL run_torch_model(neq_data, neq_pot, nequip_work, outputs)
147
148 CALL process_outputs(outputs, neq_data, neq_pot, pot_total, nequip_work)
149
150 CALL torch_dict_release(outputs)
151 CALL release_nequip_work(nequip_work)
152
153 CALL timestop(handle)
154 END SUBROUTINE e3nn_energy_store_force_virial
155
156! **************************************************************************************************
157!> \brief ...
158!> \param nequip_work ...
159!> \param atomic_kind_set ...
160!> \param particle_set ...
161!> \param local_particles ...
162!> \param cell ...
163!> \param r_pbc ...
164!> \param para_env ...
165!> \param potparm ...
166!> \param target_pot_type ...
167!> \param use_virial ...
168!> \param neq_pot ...
169!> \author Gabriele Tocci - University of Zurich
170! **************************************************************************************************
171 SUBROUTINE nequip_work_create(nequip_work, atomic_kind_set, particle_set, local_particles, cell, &
172 r_pbc, para_env, potparm, target_pot_type, use_virial, neq_pot)
173 TYPE(nequip_work_type), INTENT(OUT) :: nequip_work
174 TYPE(atomic_kind_type), POINTER :: atomic_kind_set(:)
175 TYPE(particle_type), POINTER :: particle_set(:)
176 TYPE(distribution_1d_type), POINTER :: local_particles
177 TYPE(cell_type), POINTER :: cell
178 TYPE(pos_type), DIMENSION(:), POINTER :: r_pbc
179 TYPE(mp_para_env_type), POINTER :: para_env
180 TYPE(pair_potential_pp_type), POINTER :: potparm
181 INTEGER, INTENT(IN) :: target_pot_type
182 LOGICAL, INTENT(IN) :: use_virial
183 TYPE(nequip_pot_type), INTENT(OUT), POINTER :: neq_pot
184
185 nequip_work%target_pot_type = target_pot_type
186 nequip_work%use_virial = use_virial
187 nequip_work%cell => cell
188 nequip_work%r_pbc => r_pbc
189 nequip_work%particle_set => particle_set
190 nequip_work%para_env => para_env
191 nequip_work%local_particles => local_particles
192
193 CALL get_potential_config(atomic_kind_set, potparm, target_pot_type, neq_pot)
194
195 IF (.NOT. ASSOCIATED(neq_pot)) THEN
196 RETURN
197 END IF
198
199 CALL build_kind_mapper(atomic_kind_set, neq_pot, nequip_work)
200
201 CALL init_atom_masks(nequip_work)
202
203 END SUBROUTINE nequip_work_create
204
205! **************************************************************************************************
206!> \brief ...
207!> \param nequip_work ...
208!> \author Gabriele Tocci - University of Zurich
209! **************************************************************************************************
210 SUBROUTINE release_nequip_work(nequip_work)
211 TYPE(nequip_work_type), INTENT(INOUT) :: nequip_work
212
213 IF (ALLOCATED(nequip_work%final_edges)) DEALLOCATE (nequip_work%final_edges)
214 IF (ALLOCATED(nequip_work%final_shifts)) DEALLOCATE (nequip_work%final_shifts)
215 IF (ALLOCATED(nequip_work%local_edges)) DEALLOCATE (nequip_work%local_edges)
216 IF (ALLOCATED(nequip_work%local_shifts)) DEALLOCATE (nequip_work%local_shifts)
217 IF (ALLOCATED(nequip_work%use_atom)) DEALLOCATE (nequip_work%use_atom)
218 IF (ALLOCATED(nequip_work%kind_mapper)) DEALLOCATE (nequip_work%kind_mapper)
219 IF (ALLOCATED(nequip_work%sum_energy)) DEALLOCATE (nequip_work%sum_energy)
220 NULLIFY (nequip_work%cell, nequip_work%r_pbc, nequip_work%particle_set, nequip_work%para_env, &
221 nequip_work%local_particles)
222
223 END SUBROUTINE release_nequip_work
224
225! **************************************************************************************************
226!> \brief ...
227!> \param nonbonded ...
228!> \param potparm ...
229!> \param nequip_work ...
230!> \par History
231!> Build edges and cell shifts for the GNN - [gtocci] 2026
232!> \author Gabriele Tocci - University of Zurich
233! **************************************************************************************************
234 SUBROUTINE build_local_edges_shifts(nonbonded, potparm, nequip_work)
235 TYPE(fist_neighbor_type), POINTER :: nonbonded
236 TYPE(pair_potential_pp_type), POINTER :: potparm
237 TYPE(nequip_work_type), INTENT(INOUT) :: nequip_work
238
239 INTEGER :: atom_a, atom_b, i, idx_i, idx_j, iend, &
240 igrp, ikind, ilist, ipair, istart, &
241 jkind, n_max_edges, nedges, npairs
242 INTEGER, DIMENSION(:, :), POINTER :: list
243 LOGICAL :: do_nequip_allegro
244 REAL(kind=dp) :: cutsq_ij, drij, rij(3)
245 REAL(kind=dp), DIMENSION(3) :: cell_v, cvi
246 TYPE(neighbor_kind_pairs_type), POINTER :: neighbor_kind_pair
247 TYPE(pair_potential_single_type), POINTER :: pot
248
249 n_max_edges = 0
250 DO ilist = 1, nonbonded%nlists
251 neighbor_kind_pair => nonbonded%neighbor_kind_pairs(ilist)
252 n_max_edges = n_max_edges + neighbor_kind_pair%npairs
253 END DO
254
255 ALLOCATE (nequip_work%local_edges(2, n_max_edges), nequip_work%local_shifts(3, n_max_edges))
256 nedges = 0
257
258 DO ilist = 1, nonbonded%nlists
259 neighbor_kind_pair => nonbonded%neighbor_kind_pairs(ilist)
260 npairs = neighbor_kind_pair%npairs
261 IF (npairs == 0) cycle
262
263 kind_loop: DO igrp = 1, neighbor_kind_pair%ngrp_kind
264 istart = neighbor_kind_pair%grp_kind_start(igrp)
265 iend = neighbor_kind_pair%grp_kind_end(igrp)
266 ikind = neighbor_kind_pair%ij_kind(1, igrp)
267 jkind = neighbor_kind_pair%ij_kind(2, igrp)
268
269 idx_i = nequip_work%kind_mapper(ikind)
270 idx_j = nequip_work%kind_mapper(jkind)
271
272 IF (idx_i < 1 .OR. idx_j < 1) THEN
273 ! pair involving atom not defined in the NequIP model, skipping..
274 cycle kind_loop
275 END IF
276 pot => potparm%pot(ikind, jkind)%pot
277 do_nequip_allegro = .false.
278 DO i = 1, SIZE(pot%type)
279 IF (pot%type(i) == nequip_work%target_pot_type) THEN
280 do_nequip_allegro = .true.
281 EXIT
282 END IF
283 END DO
284
285 IF (.NOT. do_nequip_allegro) cycle kind_loop
286
287 cutsq_ij = pot%set(i)%nequip%cutoff_matrix(idx_i, idx_j)
288 list => neighbor_kind_pair%list
289 cvi = neighbor_kind_pair%cell_vector
290 pot => potparm%pot(ikind, jkind)%pot
291 cell_v = matmul(nequip_work%cell%hmat, cvi)
292
293 DO ipair = istart, iend
294 atom_a = neighbor_kind_pair%list(1, ipair)
295 atom_b = neighbor_kind_pair%list(2, ipair)
296
297 rij(:) = nequip_work%r_pbc(atom_b)%r(:) - nequip_work%r_pbc(atom_a)%r(:) + cell_v
298 drij = dot_product(rij, rij)
299
300 IF (drij <= cutsq_ij) THEN
301 nedges = nedges + 1
302 nequip_work%local_edges(:, nedges) = [atom_a, atom_b]
303 nequip_work%local_shifts(:, nedges) = cvi
304 END IF
305 END DO
306 END DO kind_loop
307 END DO
308
309 IF (nedges < n_max_edges) THEN
310 block
311 INTEGER(kind=int_8), ALLOCATABLE :: tmp_idx(:, :)
312 REAL(kind=dp), ALLOCATABLE :: tmp_sft(:, :)
313
314 ALLOCATE (tmp_idx(2, nedges), tmp_sft(3, nedges))
315
316 tmp_idx(:, :) = nequip_work%local_edges(:, 1:nedges)
317 tmp_sft(:, :) = nequip_work%local_shifts(:, 1:nedges)
318
319 CALL move_alloc(tmp_idx, nequip_work%local_edges)
320 CALL move_alloc(tmp_sft, nequip_work%local_shifts)
321 END block
322 END IF
323
324 END SUBROUTINE build_local_edges_shifts
325
326! **************************************************************************************************
327!> \brief ...
328!> \param atomic_kind_set ...
329!> \param potparm ...
330!> \param target_pot_type ...
331!> \param neq_pot ...
332!> \par History
333!> Get the NequIP or Allegro potential - [gtocci] 2026
334!> \author Gabriele Tocci - University of Zurich
335! **************************************************************************************************
336 SUBROUTINE get_potential_config(atomic_kind_set, potparm, target_pot_type, neq_pot)
337 TYPE(atomic_kind_type), POINTER :: atomic_kind_set(:)
338 TYPE(pair_potential_pp_type), POINTER :: potparm
339 INTEGER, INTENT(IN) :: target_pot_type
340 TYPE(nequip_pot_type), INTENT(OUT), POINTER :: neq_pot
341
342 INTEGER :: i, ikind, jkind
343 TYPE(pair_potential_single_type), POINTER :: pot
344
345 NULLIFY (neq_pot)
346 outerloop: DO ikind = 1, SIZE(atomic_kind_set)
347 DO jkind = ikind, SIZE(atomic_kind_set)
348 pot => potparm%pot(ikind, jkind)%pot
349 DO i = 1, SIZE(pot%type)
350 IF (pot%type(i) == target_pot_type) THEN
351 neq_pot => pot%set(i)%nequip
352 EXIT outerloop
353 END IF
354 END DO
355 END DO
356 END DO outerloop
357 END SUBROUTINE get_potential_config
358
359! **************************************************************************************************
360!> \brief ...
361!> \param nequip_work ...
362!> \par History
363!> Inits masks for torch evaluation (use_atom) and MPI summation (sum_energy) - [gtocci] 2026
364!> \author Gabriele Tocci - University of Zurich
365! **************************************************************************************************
366 SUBROUTINE init_atom_masks(nequip_work)
367 TYPE(nequip_work_type), INTENT(INOUT) :: nequip_work
368
369 INTEGER :: iat, ikind, ilocal, n_atoms, n_local
370
371 IF (.NOT. ALLOCATED(nequip_work%kind_mapper)) THEN
372 cpabort("kind_mapper not initialized before init_atom_masks")
373 END IF
374
375 n_atoms = SIZE(nequip_work%particle_set)
376
377 IF (ALLOCATED(nequip_work%use_atom)) DEALLOCATE (nequip_work%use_atom)
378 ALLOCATE (nequip_work%use_atom(n_atoms))
379 nequip_work%use_atom = .false.
380
381 DO iat = 1, n_atoms
382 ikind = nequip_work%particle_set(iat)%atomic_kind%kind_number
383 IF (nequip_work%kind_mapper(ikind) > 0) THEN
384 nequip_work%use_atom(iat) = .true.
385 END IF
386 END DO
387 nequip_work%n_atoms_use = count(nequip_work%use_atom)
388
389 IF (ALLOCATED(nequip_work%sum_energy)) DEALLOCATE (nequip_work%sum_energy)
390 ALLOCATE (nequip_work%sum_energy(n_atoms))
391 nequip_work%sum_energy = .false.
392
393 IF (ASSOCIATED(nequip_work%local_particles)) THEN
394 DO ikind = 1, SIZE(nequip_work%local_particles%n_el)
395 IF (nequip_work%kind_mapper(ikind) > 0) THEN
396 n_local = nequip_work%local_particles%n_el(ikind)
397 DO ilocal = 1, n_local
398 iat = nequip_work%local_particles%list(ikind)%array(ilocal)
399 nequip_work%sum_energy(iat) = .true.
400 END DO
401 END IF
402 END DO
403 ELSE
404 nequip_work%sum_energy(:) = nequip_work%use_atom(:)
405 END IF
406
407 END SUBROUTINE init_atom_masks
408
409! **************************************************************************************************
410!> \brief ...
411!> \param atomic_kind_set ...
412!> \param neq_pot ...
413!> \param nequip_work ...
414!> \author Gabriele Tocci - University of Zurich
415! **************************************************************************************************
416 SUBROUTINE build_kind_mapper(atomic_kind_set, neq_pot, nequip_work)
417 TYPE(atomic_kind_type), POINTER :: atomic_kind_set(:)
418 TYPE(nequip_pot_type), POINTER :: neq_pot
419 TYPE(nequip_work_type), INTENT(INOUT) :: nequip_work
420
421 CHARACTER(LEN=100) :: model_sym
422 CHARACTER(LEN=default_string_length) :: kind_sym
423 INTEGER :: i, ikind, n_kinds
424
425 n_kinds = SIZE(atomic_kind_set)
426
427 IF (ALLOCATED(nequip_work%kind_mapper)) DEALLOCATE (nequip_work%kind_mapper)
428 ALLOCATE (nequip_work%kind_mapper(n_kinds))
429 nequip_work%kind_mapper = -1
430
431 DO ikind = 1, n_kinds
432 kind_sym = atomic_kind_set(ikind)%element_symbol
433 CALL uppercase(kind_sym)
434
435 DO i = 1, neq_pot%num_types
436 model_sym = neq_pot%type_names_torch(i)
437 CALL uppercase(model_sym)
438 IF (trim(kind_sym) == trim(model_sym)) THEN
439 nequip_work%kind_mapper(ikind) = i
440 EXIT
441 END IF
442 END DO
443 END DO
444 END SUBROUTINE build_kind_mapper
445
446! **************************************************************************************************
447!> \brief ...
448!> \param fist_nonbond_env ...
449!> \param neq_data ...
450!> \param pot ...
451!> \param nequip_work ...
452!> \par History
453!> load the NequIP/Allegro model, initialize forces, positions - [gtocci] 2026
454!> \author Gabriele Tocci - University of Zurich
455! **************************************************************************************************
456 SUBROUTINE setup_neq_data(fist_nonbond_env, neq_data, pot, nequip_work)
457 TYPE(fist_nonbond_env_type), POINTER :: fist_nonbond_env
458 TYPE(nequip_data_type), POINTER :: neq_data
459 TYPE(nequip_pot_type), POINTER :: pot
460 TYPE(nequip_work_type), INTENT(IN) :: nequip_work
461
462 INTEGER :: iat, iat_use, n_atoms
463
464 CALL fist_nonbond_env_get(fist_nonbond_env, nequip_data=neq_data)
465
466 IF (.NOT. ASSOCIATED(neq_data)) THEN
467 ALLOCATE (neq_data)
468 CALL fist_nonbond_env_set(fist_nonbond_env, nequip_data=neq_data)
469 NULLIFY (neq_data%use_indices, neq_data%force)
470
471 CALL torch_model_load(neq_data%model, pot%pot_file_name)
472 CALL torch_model_freeze(neq_data%model)
473 END IF
474
475 IF (ASSOCIATED(neq_data%force)) THEN
476 IF (SIZE(neq_data%force, 2) /= nequip_work%n_atoms_use) THEN
477 DEALLOCATE (neq_data%force, neq_data%use_indices)
478 END IF
479 END IF
480
481 IF (.NOT. ASSOCIATED(neq_data%force)) THEN
482 ALLOCATE (neq_data%force(3, nequip_work%n_atoms_use))
483 ALLOCATE (neq_data%use_indices(nequip_work%n_atoms_use))
484 END IF
485
486 n_atoms = SIZE(nequip_work%use_atom)
487 iat_use = 0
488 DO iat = 1, n_atoms
489 IF (nequip_work%use_atom(iat)) THEN
490 iat_use = iat_use + 1
491 neq_data%use_indices(iat_use) = iat
492 END IF
493 END DO
494 END SUBROUTINE setup_neq_data
495
496! **************************************************************************************************
497!> \brief ...
498!> \param nequip_work ...
499!> \par History
500!> Prepare edges and cell shifts for NequIP - [gtocci] 2026
501!> \author Gabriele Tocci - University of Zurich
502! **************************************************************************************************
503 SUBROUTINE prepare_edges_shifts_nequip(nequip_work)
504 TYPE(nequip_work_type), INTENT(INOUT) :: nequip_work
505
506 INTEGER :: ipair, nedges, nedges_tot
507 INTEGER(kind=int_8), ALLOCATABLE :: temp_edge_index(:, :)
508 INTEGER, ALLOCATABLE :: displ(:), displ_cell(:), edge_count(:), &
509 edge_count_cell(:)
510
511 nedges = SIZE(nequip_work%local_edges, 2)
512
513 ALLOCATE (edge_count(nequip_work%para_env%num_pe), edge_count_cell(nequip_work%para_env%num_pe))
514 ALLOCATE (displ_cell(nequip_work%para_env%num_pe), displ(nequip_work%para_env%num_pe))
515
516 CALL nequip_work%para_env%allgather(nedges, edge_count)
517 nedges_tot = sum(edge_count)
518
519 ALLOCATE (temp_edge_index(2, nedges_tot))
520 ALLOCATE (nequip_work%final_shifts(3, nedges_tot))
521
522 edge_count_cell(:) = edge_count*3
523 edge_count = edge_count*2
524 displ(1) = 0
525 displ_cell(1) = 0
526 DO ipair = 2, nequip_work%para_env%num_pe
527 displ(ipair) = displ(ipair - 1) + edge_count(ipair - 1)
528 displ_cell(ipair) = displ_cell(ipair - 1) + edge_count_cell(ipair - 1)
529 END DO
530
531 CALL nequip_work%para_env%allgatherv(nequip_work%local_shifts, nequip_work%final_shifts, edge_count_cell, displ_cell)
532 CALL nequip_work%para_env%allgatherv(nequip_work%local_edges, temp_edge_index, edge_count, displ)
533
534 ALLOCATE (nequip_work%final_edges(nedges_tot, 2))
535 nequip_work%final_edges(:, :) = transpose(temp_edge_index)
536
537 DEALLOCATE (edge_count, edge_count_cell, displ, displ_cell, temp_edge_index)
538
539 END SUBROUTINE prepare_edges_shifts_nequip
540
541! **************************************************************************************************
542!> \brief ...
543!> \param nequip_work ...
544!> \par History
545!> Prepare edges and cell shifts for Allegro - [gtocci] 2026
546!> \author Gabriele Tocci - University of Zurich
547! **************************************************************************************************
548 SUBROUTINE prepare_edges_shifts_allegro(nequip_work)
549 TYPE(nequip_work_type), INTENT(INOUT) :: nequip_work
550
551 ALLOCATE (nequip_work%final_shifts, source=nequip_work%local_shifts)
552 ALLOCATE (nequip_work%final_edges(SIZE(nequip_work%local_edges, 2), 2))
553 nequip_work%final_edges(:, :) = transpose(nequip_work%local_edges)
554 END SUBROUTINE prepare_edges_shifts_allegro
555
556! **************************************************************************************************
557!> \brief ...
558!> \param nequip_work ...
559!> \par History
560!> Build edges from cp2k global neigh lists to local/packed ones for torch - [gtocci] 2026
561!> \author Gabriele Tocci - University of Zurich
562! **************************************************************************************************
563 SUBROUTINE build_torch_edge_indexes(nequip_work)
564 TYPE(nequip_work_type), INTENT(INOUT) :: nequip_work
565
566 INTEGER :: atom_a, atom_b, i, iat, iat_use, n_atoms
567 INTEGER, ALLOCATABLE :: global_to_packed(:)
568
569 n_atoms = SIZE(nequip_work%particle_set)
570
571 ! for allegro ensure ghost atoms are included in the evaluation
572 IF (nequip_work%target_pot_type /= nequip_type .AND. &
573 nequip_work%target_pot_type /= mace_type) THEN
574 ! label atoms in the local edges
575 DO i = 1, SIZE(nequip_work%local_edges, 2)
576 atom_a = int(nequip_work%local_edges(1, i))
577 atom_b = int(nequip_work%local_edges(2, i))
578 nequip_work%use_atom(atom_a) = .true.
579 nequip_work%use_atom(atom_b) = .true.
580 END DO
581 nequip_work%n_atoms_use = count(nequip_work%use_atom)
582 END IF
583
584 ! mapping from global CP2K index to packed/local Torch index
585 ALLOCATE (global_to_packed(n_atoms))
586 global_to_packed = 0
587 iat_use = 0
588 DO iat = 1, n_atoms
589 IF (nequip_work%use_atom(iat)) THEN
590 iat_use = iat_use + 1
591 global_to_packed(iat) = iat_use
592 END IF
593 END DO
594
595 ! remap local_edges to use 0-based dense indices for torch
596 DO i = 1, SIZE(nequip_work%local_edges, 2)
597 atom_a = int(nequip_work%local_edges(1, i))
598 atom_b = int(nequip_work%local_edges(2, i))
599
600 nequip_work%local_edges(1, i) = int(global_to_packed(atom_a) - 1, kind=int_8)
601 nequip_work%local_edges(2, i) = int(global_to_packed(atom_b) - 1, kind=int_8)
602 END DO
603
604 DEALLOCATE (global_to_packed)
605
606 END SUBROUTINE build_torch_edge_indexes
607
608! **************************************************************************************************
609!> \brief ...
610!> \param neq_data ...
611!> \param pot ...
612!> \param nequip_work ...
613!> \param outputs ...
614!> \par History
615!> Run forward pass using torch api - [gtocci] 2026
616!> \author Gabriele Tocci - University of Zurich
617! **************************************************************************************************
618 SUBROUTINE run_torch_model(neq_data, pot, nequip_work, outputs)
619 TYPE(nequip_data_type), POINTER :: neq_data
620 TYPE(nequip_pot_type), POINTER :: pot
621 TYPE(nequip_work_type), INTENT(IN) :: nequip_work
622 TYPE(torch_dict_type), INTENT(OUT) :: outputs
623
624 INTEGER :: iat, iat_use, ikind
625 INTEGER(kind=int_8), ALLOCATABLE :: atom_types(:)
626 REAL(kind=dp), ALLOCATABLE :: lattice(:, :), pos(:, :)
627 TYPE(torch_dict_type) :: inputs
628 TYPE(torch_tensor_type) :: cell_t, idx_t, pos_t, shift_t, types_t
629
630 ALLOCATE (lattice(3, 3))
631 lattice(:, :) = nequip_work%cell%hmat/pot%unit_length_val
632
633 ALLOCATE (pos(3, nequip_work%n_atoms_use), atom_types(nequip_work%n_atoms_use))
634 iat_use = 0
635 DO iat = 1, SIZE(nequip_work%particle_set)
636 IF (.NOT. nequip_work%use_atom(iat)) cycle
637 iat_use = iat_use + 1
638
639 ikind = nequip_work%particle_set(iat)%atomic_kind%kind_number
640 IF (nequip_work%kind_mapper(ikind) < 1) THEN
641 CALL cp_abort(__location__, "Atom symbol not found in NequIP model!")
642 END IF
643
644 ! Convert 1-based Fortran index to 0-based PyTorch index
645 atom_types(iat_use) = nequip_work%kind_mapper(ikind) - 1
646 pos(:, iat_use) = nequip_work%r_pbc(iat)%r(:)/pot%unit_length_val
647 END DO
648
649 CALL torch_dict_create(inputs)
650
651 CALL torch_tensor_from_array(pos_t, pos)
652 CALL torch_tensor_from_array(shift_t, nequip_work%final_shifts)
653 CALL torch_tensor_from_array(cell_t, lattice)
654
655 CALL torch_dict_insert(inputs, "pos", pos_t)
656 CALL torch_dict_insert(inputs, "edge_cell_shift", shift_t)
657 CALL torch_dict_insert(inputs, "cell", cell_t)
658 CALL torch_tensor_release(pos_t)
659 CALL torch_tensor_release(shift_t)
660 CALL torch_tensor_release(cell_t)
661
662 CALL torch_tensor_from_array(idx_t, nequip_work%final_edges)
663 CALL torch_dict_insert(inputs, "edge_index", idx_t)
664 CALL torch_tensor_release(idx_t)
665
666 CALL torch_tensor_from_array(types_t, atom_types)
667 CALL torch_dict_insert(inputs, "atom_types", types_t)
668 CALL torch_tensor_release(types_t)
669
670 CALL torch_dict_create(outputs)
671 CALL torch_model_forward(neq_data%model, inputs, outputs)
672
673 CALL torch_dict_release(inputs)
674
675 IF (ALLOCATED(pos)) DEALLOCATE (pos)
676 IF (ALLOCATED(lattice)) DEALLOCATE (lattice)
677 IF (ALLOCATED(atom_types)) DEALLOCATE (atom_types)
678
679 END SUBROUTINE run_torch_model
680
681! **************************************************************************************************
682!> \brief ...
683!> \param outputs ...
684!> \param neq_data ...
685!> \param pot ...
686!> \param pot_total ...
687!> \param nequip_work ...
688!> \par History
689!> Collect potential, forces, virial - [gtocci] 2026
690!> \author Gabriele Tocci - University of Zurich
691! **************************************************************************************************
692 SUBROUTINE process_outputs(outputs, neq_data, pot, pot_total, nequip_work)
693 TYPE(torch_dict_type), INTENT(IN) :: outputs
694 TYPE(nequip_data_type), POINTER :: neq_data
695 TYPE(nequip_pot_type), POINTER :: pot
696 REAL(kind=dp), INTENT(OUT) :: pot_total
697 TYPE(nequip_work_type), INTENT(IN) :: nequip_work
698
699 INTEGER :: iat, iat_use
700 REAL(kind=dp), POINTER :: e_ptr(:, :), f_ptr(:, :), v_ptr(:, :, :)
701 TYPE(torch_tensor_type) :: t_energy, t_forces, t_virial
702
703 NULLIFY (f_ptr, e_ptr, v_ptr)
704
705 CALL torch_dict_get(outputs, "forces", t_forces)
706 CALL torch_tensor_data_ptr(t_forces, f_ptr)
707
708 neq_data%force = f_ptr*pot%unit_forces_val
709 CALL torch_tensor_release(t_forces)
710 CALL torch_dict_get(outputs, "atomic_energy", t_energy)
711 CALL torch_tensor_data_ptr(t_energy, e_ptr)
712
713 pot_total = 0.0_dp
714 DO iat_use = 1, SIZE(neq_data%use_indices)
715 iat = neq_data%use_indices(iat_use)
716 ! Only apply the local mask for Allegro models
717 IF (nequip_work%target_pot_type /= nequip_type .AND. &
718 nequip_work%target_pot_type /= mace_type) THEN
719 IF (.NOT. nequip_work%sum_energy(iat)) cycle
720 END IF
721
722 pot_total = pot_total + e_ptr(1, iat_use)
723 END DO
724 CALL torch_tensor_release(t_energy)
725 pot_total = pot_total*pot%unit_energy_val
726
727 IF (nequip_work%target_pot_type == nequip_type .OR. &
728 nequip_work%target_pot_type == mace_type) THEN
729 neq_data%force = neq_data%force/real(nequip_work%para_env%num_pe, dp)
730 pot_total = pot_total/real(nequip_work%para_env%num_pe, dp)
731 END IF
732
733 IF (nequip_work%use_virial) THEN
734 CALL torch_dict_get(outputs, "virial", t_virial)
735 CALL torch_tensor_data_ptr(t_virial, v_ptr)
736
737 neq_data%virial(:, :) = reshape(v_ptr, [3, 3])*pot%unit_energy_val
738 CALL torch_tensor_release(t_virial)
739 IF (nequip_work%target_pot_type == nequip_type .OR. &
740 nequip_work%target_pot_type == mace_type) THEN
741 neq_data%virial = neq_data%virial/real(nequip_work%para_env%num_pe, dp)
742 END IF
743 END IF
744
745 END SUBROUTINE process_outputs
746
747! **************************************************************************************************
748!> \brief ...
749!> \param fist_nonbond_env ...
750!> \param f_nonbond ...
751!> \param pv_nonbond ...
752!> \param use_virial ...
753!> \par History
754!> Sum forces, virial to nonbond - [gtocci] 2026
755!> \author Gabriele Tocci - University of Zurich
756! **************************************************************************************************
757 SUBROUTINE e3nn_add_force_virial(fist_nonbond_env, f_nonbond, pv_nonbond, use_virial)
758 TYPE(fist_nonbond_env_type), POINTER :: fist_nonbond_env
759 REAL(kind=dp), DIMENSION(:, :), INTENT(INOUT) :: f_nonbond, pv_nonbond
760 LOGICAL, INTENT(IN) :: use_virial
761
762 INTEGER :: iat, iat_use
763 TYPE(nequip_data_type), POINTER :: neq_data
764
765 CALL fist_nonbond_env_get(fist_nonbond_env, nequip_data=neq_data)
766
767 IF (use_virial) THEN
768 pv_nonbond = pv_nonbond + neq_data%virial
769 END IF
770
771 DO iat_use = 1, SIZE(neq_data%use_indices)
772 iat = neq_data%use_indices(iat_use)
773 f_nonbond(1:3, iat) = f_nonbond(1:3, iat) + neq_data%force(1:3, iat_use)
774 END DO
775
776 END SUBROUTINE e3nn_add_force_virial
777
778END MODULE manybody_e3nn
Define the atom type and its sub types.
Definition atom_types.F:15
Define the atomic kind types and their sub types.
Handles all functions related to the CELL.
Definition cell_types.F:15
stores a lists of integer that are local to a processor. The idea is that these integers represent ob...
Define the neighbor list data types and the corresponding functionality.
subroutine, public fist_nonbond_env_get(fist_nonbond_env, potparm14, potparm, nonbonded, rlist_cut, rlist_lowsq, aup, lup, ei_scale14, vdw_scale14, shift_cutoff, do_electrostatics, r_last_update, r_last_update_pbc, rshell_last_update_pbc, rcore_last_update_pbc, cell_last_update, num_update, last_update, counter, natom_types, long_range_correction, ij_kind_full_fac, eam_data, nequip_data, deepmd_data, ace_data, charges)
sets a fist_nonbond_env
subroutine, public fist_nonbond_env_set(fist_nonbond_env, potparm14, potparm, rlist_cut, rlist_lowsq, nonbonded, aup, lup, ei_scale14, vdw_scale14, shift_cutoff, do_electrostatics, r_last_update, r_last_update_pbc, rshell_last_update_pbc, rcore_last_update_pbc, cell_last_update, num_update, last_update, counter, natom_types, long_range_correction, eam_data, nequip_data, deepmd_data, ace_data, charges)
sets a fist_nonbond_env
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public int_8
Definition kinds.F:54
integer, parameter, public dp
Definition kinds.F:34
integer, parameter, public default_string_length
Definition kinds.F:57
An array-based list which grows on demand. When the internal array is full, a new array of twice the ...
Definition list.F:24
Shared TorchScript evaluation path for e3nn-based equivariant message-passing potentials (NequIP,...
subroutine, public e3nn_energy_store_force_virial(nonbonded, particle_set, local_particles, cell, atomic_kind_set, potparm, r_last_update_pbc, pot_total, fist_nonbond_env, para_env, use_virial, target_pot_type)
...
subroutine, public e3nn_add_force_virial(fist_nonbond_env, f_nonbond, pv_nonbond, use_virial)
...
Interface to the message passing library MPI.
integer, parameter, public nequip_type
integer, parameter, public mace_type
Define the data structure for the particle information.
Utilities for string manipulations.
elemental subroutine, public uppercase(string)
Convert all lower case characters in a string to upper case.
subroutine, public torch_dict_release(dict)
Releases a Torch dictionary and all its ressources.
Definition torch_api.F:1842
subroutine, public torch_dict_get(dict, key, tensor)
Retrieves a Torch tensor from a Torch dictionary.
Definition torch_api.F:1808
subroutine, public torch_model_load(model, filename)
Loads a Torch model from given "*.pth" file. (In Torch lingo models are called modules)
Definition torch_api.F:1866
subroutine, public torch_dict_create(dict)
Creates an empty Torch dictionary.
Definition torch_api.F:1725
subroutine, public torch_model_freeze(model)
Freeze the given Torch model: applies generic optimization that speed up model. See https://pytorch....
Definition torch_api.F:2253
subroutine, public torch_dict_insert(dict, key, tensor)
Inserts a Torch tensor into a Torch dictionary.
Definition torch_api.F:1776
subroutine, public torch_tensor_release(tensor)
Releases a Torch tensor and all its ressources.
Definition torch_api.F:1701
subroutine, public torch_model_forward(model, inputs, outputs)
Evaluates the given Torch model.
Definition torch_api.F:1996
Provides all information about an atomic kind.
Type defining parameters related to the simulation cell.
Definition cell_types.F:60
structure to store local (to a processor) ordered lists of integers.
stores all the informations relevant to an mpi environment