\(\renewcommand{\AA}{\text{Å}}\)

# 2.3.9. Scatter/gather operations

    data = lmp.gather_atoms(name,dtype,count)  # return per-atom property of all atoms gathered into data, ordered by atom ID
                                               # name = "x", "q", "type", etc
    data = lmp.gather_atoms_concat(name,dtype,count)  # ditto, but concatenated atom values from each proc (unordered)
    data = lmp.gather_atoms_subset(name,dtype,count,ndata,ids)  # ditto, but for subset of Ndata atoms with IDs

    lmp.scatter_atoms(name,dtype,count,data)   # scatter per-atom property to all atoms from data, ordered by atom ID
                                               # name = "x", "q", "type", etc
                                               # count = # of per-atom values, 1 or 3, etc

    lmp.scatter_atoms_subset(name,dtype,count,ndata,ids,data)  # ditto, but for subset of Ndata atoms with IDs

The gather methods collect peratom info of the requested type (atom coords, atom types, forces, etc) from all processors, and returns the same vector of values to each calling processor. The scatter functions do the inverse. They distribute a vector of peratom values, passed by all calling processors, to individual atoms, which may be owned by different processors.

The dtype parameter is 0 for `int` values and 1 for `double` values. The count parameter is 1 for per-atom vectors like “type” or “q” and 3 for per-atom arrays like “x”, “v”, “f”. Use count = 3 with name = “image” if you want the single integer storing the image flags unpacked into 3 components (“x”, “y”, and “z”).

Note that the data returned by the gather methods, e.g. `gather_atoms("x")`, is different from the data structure returned by `extract_atom("x")` in four ways. (1) `gather_atoms()` returns a vector which you index as x[i]; `extract_atom()` returns an array which you index as x[i][j]. (2) `gather_atoms()` orders the atoms by atom ID while `extract_atom()` does not. (3) `gather_atoms()` returns a list of all atoms in the simulation; `extract_atoms()` returns just the atoms local to each processor. (4) Finally, the `gather_atoms()` data structure is a copy of the atom coords stored internally in LAMMPS, whereas `extract_atom()` returns an array that effectively points directly to the internal data. This means you can change values inside LAMMPS from Python by assigning a new values to the `extract_atom()` array. To do this with the `gather_atoms()` vector, you need to change values in the vector, then invoke the `scatter_atoms("x")` method.

For the scatter methods, the array of coordinates passed to must be a ctypes vector of ints or doubles, allocated and initialized something like this:

    from ctypes import c_double
    natoms = lmp.get_natoms()
    n3 = 3*natoms
    x = (n3*c_double)()
    x[0] = x coord of atom with ID 1
    x[1] = y coord of atom with ID 1
    x[2] = z coord of atom with ID 1
    x[3] = x coord of atom with ID 2
    ...
    x[n3-1] = z coord of atom with ID natoms
    lmp.scatter_atoms("x", 1, 3, x)

The coordinates can also be provided as arguments to the initializer of x:

    from ctypes import c_double
    natoms = 2
    n3 = 3*natoms
    # init in constructor
    x = (n3*c_double)(0.0, 0.0, 0.0, 1.0, 1.0, 1.0)
    lmp.scatter_atoms("x", 1, 3, x)
    # or using a list
    coords = [1.0, 2.0, 3.0, -3.0, -2.0, -1.0]
    x = (c_double*len(coords))(*coords)

Alternatively, you can just change values in the vector returned by the gather methods, since they are also ctypes vectors.