Energy / Force Fields#
Avogadro allows scripts to calculate energies and gradients for optimizing geometry.
The script must handle the following command line arguments:
--metadataPrint metadata describing the script--display-namePrint the name to display in user options--lang enOptionally respond to language translation codes--file FILECalculate energies and gradients for a particular molecule
With the exception of --metadata and --display-name options
Identify the Script with --metadata#
Running the script with the --metadata option should print a JSON object
of the following form:
{
"inputFormat": "pdb",
"identifier": "Unique Name"
"name": "User-Friendly Name",
"description": "Description of method or citation.",
"elements": "1,6-9"
"unitCell": False,
"gradients": True,
"ion": False,
"radical": False,
}
Details:
inputFormatindicates the molecular file format that Avogadro should supply to the script. Allowed values are"cml","cjson","pdb","sdf"or"xyz". Instead of"sdf", the extensions"mdl"or"mol"are also allowed.identifieris a unique identifier. The value must only be unique amongst script charges, since it is used internally.nameis a user-friendly name for the method, which will be used in menus.descriptionis an optional description of the method, along with any relevant help text for users.elementsis a list indicating the atomic numbers supported by the method. Both commas1, 6, 7and ranges1-86are supported.unitCellis eitherTrueorFalseas to whether systems with lattice vectors are supported by the method.gradientsindicatesTrueif analytical gradients will be calculated by the method orFalseif Avogadro should calculate numeric gradients.ionindicatesTrueif the method can handle systems with non-zero total charge (e.g., cations, anions, etc.)radicalindicatesTrueif the method can handle systems with unpaired spins (i.e., spin multiplicity beyond singlet).
- Optional members are:
description
Make sure to specify the elements list correctly. Avogadro will automatically
exclude a script in the list of available methods if a molecule contains
elements not in the list (e.g., if it does not support metals), or based on the unitCell, ion, and radical fields.
Calculating Energies and Gradients#
Avogadro will write a temporary file in the specified file format and supply the filename when calling the script, e.g. script.py -f tempfile.mol.
For example a script using Open Babel might use code like this:
# we declared "inputFormat": "cml" in the metadata
def run(filename):
# we get the molecule from the supplied filename
# in cjson format (it's a temporary file created by Avogadro)
mol = next(pybel.readfile("cml", filename))
ff = pybel._forcefields["uff"]
success = ff.Setup(mol.OBMol)
if not success:
# should never happen, but just in case
sys.exit("UFF force field setup failed")
Another example is to use cjson and get elements and coordinates:
def run(filename):
# we get the molecule from the supplied filename
# in cjson format (it's a temporary file created by Avogadro)
with open(filename, "r") as f:
mol_cjson = json.load(f)
# get the atomic numbers
atoms = np.array(mol_cjson["atoms"]["elements"]["number"])
# get the coordinates in a list of [ [x, y, z], [x, y, z] … ]
coord_list = mol_cjson["atoms"]["coords"]["3d"]
coordinates = np.array(coord_list, dtype=float).reshape(-1, 3)
After reading coordinates from the tempory filename on the command-line arguments, Avogadro will supply updated coordinates through standard input and expect energies and gradients on the standard output.
# we loop forever - Avogadro will kill the process when done
num_atoms = len(mol.atoms)
while True:
# read new coordinates from stdin
for i in range(num_atoms):
coordinates = np.fromstring(input(), sep=" ")
atom = mol.atoms[i]
atom.OBAtom.SetVector(coordinates[0], coordinates[1], coordinates[2])
# update the molecule geometry for the next energy
ff.SetCoordinates(mol.OBMol)
# first print the energy of these coordinates
energy = ff.Energy(True) # in kJ/mol
print("AvogadroEnergy:", energy) # in kJ/mol
# now print the gradient on each atom
print("AvogadroGradient:")
for atom in mol.atoms:
grad = ff.GetGradient(atom.OBAtom)
print(-1.0*grad.GetX(), -1.0*grad.GetY(), -1.0*grad.GetZ())
Note that Avogadro expects energies and gradients with the indicated tags:
AvogadroEnergy: [energy]
AvogadroGradient:
x y z
x y z
x y z
x y z
…
Unit Cells, Total Charge, and Spin Multiplicity#
If your method involves unit cells, total charge, or spin multiplicity, the cjson format is likely your best option.
Lattice Vectors and Fractional Coordinates#
The cjson format from Avogadro will provide both real-space Cartesian coordinates as cjson["atoms"]["coords"]["3d"] and fractional coordinates as cjson["atoms"]["coords"]["3dFractional"]. For both cases, the coordinates are supplied as a 1D array, which can be reshaped via numpy, e.g. np.array(coord_list, dtype=float).reshape(-1, 3).
The unit cell parameters and lattice vectors are also available in cjson:
"unitCell": {
"a": 4.0862,
"alpha": 90.0,
"b": 4.0862,
"beta": 90.0,
"c": 4.0862,
"cellVectors": [
4.0862,
0.0,
0.0,
0.0,
4.0862,
0.0,
0.0,
0.0,
4.0862
],
"gamma": 90.0
}
For example, if your script needs the lattice vectors, you can use cjson["unitCell"]["cellVectors"] to get a 1D list of the vectors and can reshape via np.array(cell_list, dtype=float).reshape(3,3). Alternatively, you can access:
a = cjson["unitCell"]["a"]b = cjson["unitCell"]["a"]c = cjson["unitCell"]["a"]alpha = cjson["unitCell"]["alpha"]beta = cjson["unitCell"]["beta"]gamma = cjson["unitCell"]["gamma"]