final tweaks

This commit is contained in:
Steve Plimpton
2022-02-22 09:20:19 -07:00
parent 15acfde91e
commit 7ac1bd1180

View File

@ -29,33 +29,36 @@ is updated (e.g. their positions) and this updated information needs to
be **copied** to the corresponding ghost atoms.
And second, *reverse communication* which sends ghost atom information
from each processor to the owning processor to **accumulate** (sum) the
values with the corresponding owned atoms. The need for this arises
when data is computed and also stored with ghost atoms (e.g. forces when
using a "half" neighbor list) and thus those terms need to be added to
their corresponding atoms on the process where they are "owned" atoms.
Please note, that with the :doc:`newton off <newton>` setting this does
not happen and the neighbor lists are constructed that these pairs are
computed on both MPI processes containing one of the atoms and only the
data pertaining to the local atom is stored.
from each processor to the owning processor to **accumulate** (sum)
the values with the corresponding owned atoms. The need for this
arises when data is computed and also stored with ghost atoms
(e.g. forces when using a "half" neighbor list) and thus those terms
need to be added to their corresponding atoms on the process where
they are "owned" atoms. Please note, that with the :doc:`newton off
<newton>` setting this does not happen and the neighbor lists are
constructed so that these interactions are computed on both MPI
processes containing one of the atoms and only the data pertaining to
the local atom is stored.
The time-integration classes in LAMMPS invoke these operations each
timestep via the *forward_comm()* and *reverse_comm()* methods in the
*Comm* class. Which per-atom data is communicated depends on the
currently used :doc:`atom style <atom_style>` and whether
:doc:`comm_modify vel <comm_modify>` setting is "no" (default) or "yes".
:doc:`comm_modify vel <comm_modify>` setting is "no" (default) or
"yes".
Similarly, *Pair* style classes can invoke the *forward_comm(this)*
and *reverse_comm(this)* methods in the *Comm* class to perform the
same operations on per-atom data that is stored within the pair style
class. Note that this function requires passing the ``this`` pointer
as the first argument to be able to call the "pack" and "unpack" functions
discussed below. An example for the use of these functions are many-body pair
styles like the embedded-atom method (EAM) which compute intermediate
values in the first part of the compute() function that need to be
stored on both, owned and ghost atoms for the second part of the force
computing. The *Comm* class methods perform the MPI communication for
buffers of per-atom data. They "call back" to the *Pair* class so it can *pack*
same operations on per-atom data that is generated and stored within
the pair style class. Note that this function requires passing the
``this`` pointer as the first argument to enable the *Comm* class to
call the "pack" and "unpack" functions discussed below. An example of
the use of these functions are many-body pair styles like the
embedded-atom method (EAM) which compute intermediate values in the
first part of the compute() function that need to be stored by both
owned and ghost atoms for the second part of the force computation.
The *Comm* class methods perform the MPI communication for buffers of
per-atom data. They "call back" to the *Pair* class so it can *pack*
or *unpack* the buffer with data the *Pair* class owns. There are 4
such methods that the *Pair* class must define, assuming it uses both
forward and reverse communication:
@ -65,32 +68,37 @@ forward and reverse communication:
* pack_reverse_comm()
* unpack_reverse_comm()
The arguments to these methods include the buffer and a list of atoms to
pack or unpack. The *Pair* class also must set the *comm_forward* and
*comm_reverse* variables which store the number of values it will store
in the communication buffers for each operation. This means it can
choose to store multiple per-atom values in the buffer, if desired, and
they will be communicated together to minimize communication overhead.
The communication buffers are defined as arrays containing ``double``
values. To correctly store integers that may be 64-bit (bigint,
tagint, imageint) in the buffer, you need to use the `ubuf union
<Communication buffer coding with ubuf>`_.
The arguments to these methods include the buffer and a list of atoms
to pack or unpack. The *Pair* class also must set the *comm_forward*
and *comm_reverse* variables which store the number of values stored
in the communication buffers for each operation. This means, if
desired, it can choose to store multiple per-atom values in the
buffer, and they will be communicated together to minimize
communication overhead. The communication buffers are defined vectors
containing ``double`` values. To correctly store integers that may be
64-bit (bigint, tagint, imageint) in the buffer, you need to use the
`ubuf union <Communication buffer coding with ubuf>`_ construct.
The *Fix*, *Compute*, and *Dump* classes can also invoke the same kind of
forward and reverse communication operations using *Comm* class methods.
The same pack/unpack methods and comm_forward/comm_reverse variables
must be defined by the *Fix* or *Compute* class.
The *Fix*, *Compute*, and *Dump* classes can also invoke the same kind
of forward and reverse communication operations using the same *Comm*
class methods. Likewise the same pack/unpack methods and
comm_forward/comm_reverse variables must be defined by the calling
*Fix*, *Compute*, or *Dump* class.
For *Fix* classes there is an optional second argument to the
*forward_comm()* and *reverse_comm()* call which are used when there are
communications with buffer sizes different from the value set in the
comm_forward/comm_reverse variables. For this to work a class member
variable must be set to indicate to the pack/unpack routines which
which data needs to be packed into and unpacked from the buffer.
*forward_comm()* and *reverse_comm()* call which can be used when the
fix performs multiple modes of communication, with different numbers
of values per atom. The fix should set the *comm_forward* and
*comm_reverse* variables to the maximum value, but can invoke the
communication for a particulard mode with a smaller value. For this
to work, the *pack_forward_comm()*, etc methods typically use a class
member variable to choose which values to pack/unpack into/from the
buffer.
Finally, for reverse communications in *Fix* classes there is also
the *reverse_comm_variable()* method that allows the communication to have
a different amount of data per-atom. It invokes these corresponding callback methods:
Finally, for reverse communications in *Fix* classes there is also the
*reverse_comm_variable()* method that allows the communication to have
a different amount of data per-atom. It invokes these corresponding
callback methods:
* pack_reverse_comm_size()
* unpack_reverse_comm_size()
@ -133,8 +141,8 @@ processor sends to the first processor, and the first processor
receives from the last processor.
Invoking the *ring()* method passes each processor's buffer in *P*
steps around the ring. At each step a *callback* method (provided as
an argument to ring()) in the caller is invoked. This allows each
steps around the ring. At each step a *callback* method, provided as
an argument to ring(), in the caller is invoked. This allows each
processor to examine the data buffer provided by every other
processor. It may extract values needed by its atoms from the
buffers, or it may alter placeholder values in the buffer. In the
@ -145,18 +153,18 @@ Note that the *ring* operation is similar to an MPI_Alltoall()
operation where every processor effectively sends and receives data to
every other processor. The difference is that the *ring* operation
does it one step at a time, so the total volume of data does not need
to be stored by every processor. However, *ring* is also less
efficient than MPI_Alltoall() because of the *P* stages required. So
it is typically only suitable for small data buffers and occasional
operations that are not time-critical.
to be stored by every processor. However, the *ring* operation is
also less efficient than MPI_Alltoall() because of the *P* stages
required. So it is typically only suitable for small data buffers and
occasional operations that are not time-critical.
Irregular operation
===================
The *irregular* operation is provided by the *Irregular* class.
Irregular communication is when each processor knows what data it
needs to send to what processor, but does not know what processors are
sending it data. An example for LAMMPS is when load-balancing is
The *irregular* operation is provided by the *Irregular* class. What
LAMMPS terms irregular communication is when each processor knows what
data it needs to send to what processor, but does not know what
processors are sending it data. An example is when load-balancing is
performed and each processor needs to send some of its atoms to new
processors.
@ -194,7 +202,7 @@ communicated to a new owning processor.
Rendezvous operation
====================
Finally, the *rendezvous* operation is invoked vie the *rendezvous()*
Finally, the *rendezvous* operation is invoked via the *rendezvous()*
method in the *Comm* class. Depending on how much communication is
needed and how many processors a LAMMPS simulation is running on, it
can be a much more efficient choice than the *ring()* method. It uses
@ -203,14 +211,15 @@ communication. The rendezvous algorithm is described in detail in
:ref:`(Plimpton) <Plimpton>`, including some LAMMPS use cases.
For the *rendezvous()* method, each processor specifies a list of *N*
datums to send, each to a specified processor. Internally, this
communication is performed as an irregular operation. The received
datums are returned to the caller via invocation of *callback*
function, provided as an argument to rendezvous(). The caller can
then process the received datums and (optionally) assemble a new list
of datums to communicate to a new list of specific processors. When
the callback function exits, the *rendezvous()* method performs a
second irregular communication on the new list of datums.
datums to send and which processor to send each of them to.
Internally, this communication is performed as an irregular operation.
The received datums are returned to the caller via invocation of
*callback* function, provided as an argument to *rendezvous()*. The
caller can then process the received datums and (optionally) assemble
a new list of datums to communicate to a new list of specific
processors. When the callback function exits, the *rendezvous()*
method performs a second irregular communication on the new list of
datums.
Examples in LAMMPS of use of the *rendezvous* operation are the
:doc:`fix rigid/small <fix_rigid>` and :doc:`fix shake