# ParallelEnergyAndForces¶

ParallelEnergyAndForces(configurations, processes_per_configuration, filenames=None, master_only=True)

Evaluates the energy and forces for a list of configurations in parallel.

Parameters: configurations (list of configurations (MoleculeConfiguration | BulkConfiguration | SurfaceConfiguration | DeviceConfiguration)) – A list of configurations with attached calculators. processes_per_configuration (int) – The number of MPI processes to use for calculating the energy and forces on each configuration. log_filenames (list of strings) – A list of filenames to log each calculation to. It must be the same length as the configurations argument. If None is given, then logging will be performed to stdout. Default: All calculations are logged to stdout master_only (bool) – Controls if the master process should be the only rank allowed to write to the log. Default: True A tuple of energies and forces as PhysicalQuantity arrays. tuple

## Usage Examples¶

Calculate the potential energy curve and forces for a hydrogen molecule in parallel. At the end of the script, a table of internuclear distances, energies, and the force is printed to the screen.

# Make a list to hold the configurations.
configurations = []

# Loop over a list of distances between 0.3 and 5.0 Angstrom.
distances = numpy.linspace(0.3, 5.0, 20)
for distance in distances:
# Define elements
elements = [Hydrogen, Hydrogen]

# Define coordinates
cartesian_coordinates = [[ 0.0, 0.0, 0.0 ],
[ distance, 0.0, 0.0 ]]*Angstrom

# Set up configuration
molecule_configuration = MoleculeConfiguration(
elements=elements,
cartesian_coordinates=cartesian_coordinates
)

# Define a calculator
molecule_configuration.setCalculator(LCAOCalculator())

# Add the configuration to the list of configurations.
configurations.append(molecule_configuration)

# Compute the total energy.
total_energy = TotalEnergy(configuration)
# Save the result to a file.
nlsave('total_energy_%i.hdf5'%index, configuration)
# Return the calculated total energy.

# Define a list of filenames to save the logging output from each calculation to.
filenames = [ 'total_energy_%i.log' % i for i in range(len(configurations)) ]

# Calculate the energy of each configuration. Each calculation will use 2 MPI processes.
energies = ParallelMapConfigurations(
configurations,
processes_per_configuration=2,
filenames=filenames,
)

# Only print on the master process. This prevents the table from being printed multiple times.
if processIsMaster():
print('%10s %12s' % ('distance', 'energy'))
for i in range(len(configurations)):
print('%10.4f %12.3e' % (distances[i], energies[i].inUnitsOf(eV)))



parallelmapconfigurations.py

## Notes¶

It is important to properly coordinate the total number of MPI processes, the processes_per_configuration argument, and the number of configurations. When ParallelEnergyAndForces is called, the MPI processes are divided up into NLEngine.numberOfProcesses()/processes_per_configuration sized groups. For example, if there are 8 MPI processes and processes_per_configuration=2, then 4 groups will be made. However, if there are only 2 configurations in the configurations list then 2 of those groups will be idle.

Ideally, one would pick processes_per_configuration to be the largest number of processes that a single DFT calculation runs efficiently on. This generally depends on a number of variables including the number of atoms, basis set size, computer hardware, etc. Then, the total number of MPI processes should be an integer multiple of processes_per_configuration.

This function can be used with ATK-ForceFiled calculators as well. However, ATK-ForceField does not currently make use of MPI. This means that processes_per_configuration should always be set to 1 in order not to have idle processes.