Use pARTn with a non-supported E/F engine
pARTn can also be used with E/F engines which are not directly supported through an interface. To do that, a separate program/script has to be implemented, which calls the desired E/F engine, and executes prescribed ARTn steps.
Note
In order to use it, pARTn can be compiled without any specific engine, i.e. it suffices to:
./configure && make lib
or:
cmake -B <my_builddir> && cmake --build <my_builddir>
The underlying routine here is called artn_step(), which gives the displacement vector associated to the current step in the ARTn algorithm.
Any other interaction with pARTn is done through the API.
Pseudocode
To use the function in action, a pseudocode might look something like (emphasized lines for setting the engine units, and the loop over ARTn steps):
1use artn_api, only: artn_rp => DP
2use artn_api, only: artn_create, artn_set, artn_extract, artn_step, get_error
3implicit none
4type( other_engine ) :: my_engine
5integer :: maxsteps
6integer :: ierr
7logical :: lerr, lconv
8real( artn_rp ), allocatable :: displ_vec(:,:)
9
10maxsteps = 500
11
12!! initialize your E/F engine of choice
13my_engine = engine( ... )
14
15!! create artn instance
16ierr = artn_create()
17
18!! set your parameters to artn (remember to check ierr value)
19call artn_set( "engine_units", "lammps/metal", ierr=ierr )
20call artn_set( "verbose", 0, ierr=ierr )
21call artn_set( "struc_format_out", "none", ierr=ierr )
22call artn_set( "nevalf_max", maxsteps, ierr=ierr )
23call artn_set( "forc_thr", force_thr, ierr=ierr )
24!! etc ...
25
26!! call setup (not strictly necessary, but gives option to modify the runparams from here)
27call setup_artn2( nat, lerr )
28
29!! allocate the array for dispalcement vector
30allocate( displ_vec(1:3, 1:nat) )
31
32istep_: do istep = 1, maxsteps
33
34 !! get energy, force of current atomic configuration
35 call my_engine% run( positions, types, box, ... , energy, force )
36
37 !! call artn_step, remember it is only on one cpu (``me==0``). Units are as specified above
38 if( me==0 ) call artn_step( nat, energy, force, types, positions, box, if_pos, displ_vec, lconv )
39
40 !! bcast the lconv flag, if mpi
41 call mpi_bcast( lconv, .... )
42
43 !! exit the loop if convergence flag is .true.
44 if( lconv ) exit istep_
45
46 !! apply displacement vector (it's only available on ``me==0`` in mpi), in proper precision
47 if( me==0 ) positions = positions + real( displ_vec, kind=positions )
48
49 !! bcast the new positions
50 call mpi_bcast( positions, ... )
51
52end do istep_
53
54!! check for error (all data is only on ``me==0`` in mpi):
55if( me==0 ) ierr = artn_extract( "has_error", lsuccess )
56call mpi_bcast( lsuccess, ... )
57
58if( lsuccess ) then
59 !! extract data from ``me===0`` and bcast if needed
60 !! etcetc ...
61else
62 !! get errmsg
63 if(me==0) then
64 ierr = get_error( msg )
65 write(*,*) msg
66 end if
67 !! whatever error management ...
68 call mpi_barrier( ... )
69 call mpi_abort( ... )
70end if
(under construction. Check the fortran version in the meantime, it should be similar for C)
Pseudocode1import pypARTn 2 3## initialize artn, specify the engine as "other" 4a = pypARTn.artn( engine="other" ) 5 6# set the set of units to use: lammps/metal is Ang, eV 7a.set_param( "engine_units", "lammps/metal") 8 9# set some other params to artn: 10a.set_param( "verbose", 0) 11a.set_param( "forc_thr", 1e-2) 12a.set_param( "ninit", 2) 13a.set_param( "push_step_size", 0.1) 14a.set_param( "nnewchance", 5) 15a.set_param( "struc_format_out", "none") 16 17# create an initial push vector 18push_in = np.ndarray( [nat,3], dtype=np.float64 ) 19push_in = .... 20a.set("push_init", push_in ) 21 22maxsteps = 500 23a.set_param( "nevalf_max", maxsteps-1 ) 24 25## loop over the steps 26for istep in range( maxsteps ): 27 28 ## compute E/F with the engine of choice, with whatever arguments are needed, 29 ## return energy and force of current atomic configuration 30 my_engine.run( positions, types, ... , energy, force ) 31 32 ## get next displacement vector from artn. Use the previously computed 33 ## energy and force, they should be in units of Ang, eV (eqv. to "lammps/metal") 34 displ_vec, lconv = a.next_displ( nat, energy, force, types, positions, box, if_pos ) 35 36 ## `lconv` is the convergence flag, is `True` when converged, or error. 37 if( lconv ): 38 break 39 40 ## `displ_vec` is the next displacement to make 41 positions = positions + displ_vec 42 43# end of run, check for error 44if( a.extract("has_error") ): 45 ierr, errmsg=a.get_error() 46 print( errmsg ) 47 48# extract desired data 49a.extract( ... ) 50 51# etcetc. 52 53## If another exploration will be launched in the same script, 54## the internal data and parameters of ARTn need to be cleaned before-hand: 55## (this will keep the input parameters as previously defined) 56#a.clean() 57 58## If you wish to completely clean (destroy) the ARTn instance, including 59## resetting the input parameters to their default values, call: 60a.destroy()
Function reference
From fortran, we directly call artn_step() routine.
It has the following documentation:
- group artn_step
Functions
- subroutine, public artn_step (nat, etot, eng_force, ityp, pos, box, if_pos, displ_vec, lconv)
Routine to perform single step of artn research.
- Parameters:
nat – [in] number of atoms
etot – [in] total energy of the engine
eng_force – [in] force calculated by the engine
ityp – [in] list of type of atoms
pos – [in] atomic position
box – [in] lattice vectors in columns
if_pos – [in] list of fixed atomic degrees of freedom. 3 integers per atom, value 0 to fix the atom in corresponding direction, or value 1 to allow move.
displ_vec – [out] displacement vector communicated to move_mode
lconv – [out] flag for controlling convergence
There is a C-bound routine to artn_step(), with the same name:
- group c_artn_step
Functions
-
subroutine artn_cstep(cnat, cetot, ceng_force, ctyp, cpos, cbox, cif_pos, cdispl_vec, clconv)
C wrapper to artn_step()
C-header
void artn_step( const int cnat, const double cetot, double *const ceng_force, int const *ctyp, double *const cpos, const double *cbox, const int *cif_pos, double *cdispl_vec, bool *clconv);
- Parameters:
cnat – [in] number of atoms
cetot – [in] total energy of the engine
ceng_force – [in] force calculated by the engine
ctyp – [in] list of type of atoms
cpos – [in] atomic position
cbox – [in] lattice vectors in columns
cif_pos – [in] list of fixed atomic degrees of freedom. 3 integers per atom, value 0 to fix the atom in corresponding direction, or value 1 to allow move.
cdispl_vec – [out] displacement vector communicated to move_mode
clconv – [out] flag for controlling convergence
-
subroutine artn_cstep(cnat, cetot, ceng_force, ctyp, cpos, cbox, cif_pos, cdispl_vec, clconv)
In Python, the wrapper to artn_step() routine is called artn.next_displ(), and has the documentation:
- class pypARTn.artn(engine=None, shlib=None)[source]
- next_displ(nat, etot, force, typ, pos, box, if_pos)[source]
Obtain the next displacement vector according to ARTn algorithm. This calls the artn_step routine.
The units of etot and force input should be in accordance to the engine_units parameter.
NOTE: ARTn has an internal state, which will advance assuming the displacement returned from this function is the displacement that is really applied. Take care when performing displacements other than the one returned from this routine.
NOTE: At convergence, the system remains at the last position. It does NOT return to the initial structure.
== input: ==
- Parameters:
nat (integer) – number of atoms
etot (float) – current total energy, in units specified by engine_units parameter
force (float [nat,3]) – current force vector on all atoms, in units specified by engine_units parameter
typ (integer [nat]) – integer atomic types
pos (float [nat,3]) – current atomic positions
box (float [3,3]) – lattice vectors in rows
if_pos (integer [nat,3]) – indicate fixed atoms (analogous to QE), each atom gets integer 3-vector {u,v,j}, where any value 0 means the atom is fixed in that direction, and value 1 means the atom is free to move (i.e. mask on force).
== output: ==
- Parameters:
dr (float [nat,3]) – next displacement vector according to ARTn
lconv (logical) – convergence flag, true when system is converged (or error), false otherwise
== Example: ==
>>> ## loop for a number of steps >>> for istep in range( maxstep ): >>> ## compute energy and force for current positions >>> ## ... >>> >>> ## get artn displacement >>> dr, lconv = artn.next_displ( nat, Etot, Force, typ, pos, box, if_pos ) >>> >>> ## convergence criterion achieved >>> if( lconv ): >>> break >>> >>> ## apply displacement >>> pos += dr >>> >>> ## check for error >>> if( artn.extract("has_error") ): >>> ierr, errmsg=artn.get_error() >>> print(errmsg)